-
-
Made with copious amounts of caffeeine
-
-
- TODO: Write some funny text here
-
-
-
-
I hate it here
-
-
- Bottom text
-
-
-
-
-
-
Is this the real life?
-
-
- Something something fuck poettering
-
-
-
-
Open source or something
-
-
- I forgor
-
-
-
+ {{template "home_forgejo" .}}
{{template "base/footer" .}}
diff --git a/modules/services/forgejo/templates/home_forgejo.tmpl b/modules/services/forgejo/templates/home_forgejo.tmpl
new file mode 100644
index 0000000..d5d18c7
--- /dev/null
+++ b/modules/services/forgejo/templates/home_forgejo.tmpl
@@ -0,0 +1,36 @@
+
+
+
+
+ {{ctx.Locale.Tr "startpage.install_desc" "https://forgejo.org/download/#installation-from-binary" "https://forgejo.org/download/#container-image" "https://forgejo.org/download"}}
+
+
+
+
+
+ {{ctx.Locale.Tr "startpage.platform_desc"}}
+
+
+
+
+
+
+
+ {{ctx.Locale.Tr "startpage.lightweight_desc"}}
+
+
+
+
+
+ {{ctx.Locale.Tr "startpage.license_desc" "https://forgejo.org/download" "https://codeberg.org/forgejo/forgejo"}}
+
+
+
diff --git a/modules/services/grafana/grafana.mod.nix b/modules/services/grafana/grafana.mod.nix
new file mode 100644
index 0000000..5652656
--- /dev/null
+++ b/modules/services/grafana/grafana.mod.nix
@@ -0,0 +1,103 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (builtins) fetchurl;
+ inherit (lib.modules) mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ cfg = config.modules.system.services.grafana;
+ domain = "info.copeberg.org";
+ port = 4021;
+in
+{
+ options.modules.system.services.grafana.enable = mkEnableOption "Grafana, a graphing service";
+
+ config = mkIf cfg.enable {
+ networking.firewall.allowedTCPPorts = [ config.services.grafana.settings.server.http_port ];
+
+ modules.system.services.database.postgresql.enable = true;
+
+ services.grafana = {
+ enable = true;
+ package = pkgs.grafana;
+
+ settings = {
+ server = {
+ http_addr = "127.0.0.1";
+ http_port = port;
+
+ root_url = "https://${domain}";
+ inherit domain;
+ enforce_domain = true;
+ };
+ database = {
+ type = "postgres";
+ host = "/run/postgresql";
+ name = "grafana";
+ user = "grafana";
+ ssl_mode = "disable";
+ };
+
+ analytics = {
+ reporting_enabled = false;
+ check_for_updates = false;
+ };
+
+ users.allow_signup = false;
+ };
+ provision = {
+ enable = true;
+ datasources.settings = {
+ datasources = [
+ (mkIf config.modules.system.services.prometheus.enable {
+ name = "Prometheus";
+ type = "prometheus";
+ access = "proxy";
+ url = "http://127.0.0.1:${toString config.services.prometheus.port}";
+ isDefault = true;
+ })
+
+ (mkIf config.modules.system.services.database.postgresql.enable {
+ name = "PostgreSQL";
+ type = "postgres";
+ access = "proxy";
+ url = "127.0.0.1:${toString config.services.prometheus.exporters.postgres.port}";
+ })
+ ];
+ };
+ dashboards.settings.providers = [
+ {
+ # taken from https://grafana.com/grafana/dashboards/1860-node-exporter-full/
+ name = "system-status";
+ options.path = fetchurl {
+ url = "https://grafana.com/api/dashboards/1860/revisions/40/download";
+ sha256 = "sha256-zTsS/UEX6W8+qK3l2GtvdDfmwS8eVnnyZxZ++LtRLBA=";
+ };
+ }
+ {
+ # taken from https://grafana.com/grafana/dashboards/9628-postgresql-database/
+ name = "PostgreSQL-status";
+ options.path = fetchurl {
+ url = "https://grafana.com/api/dashboards/9628/revisions/8/download";
+ sha256 = "sha256-UhusNAZbyt7fJV/DhFUK4FKOmnTpG0R15YO2r+nDnMc=";
+ };
+ }
+ ];
+ };
+ };
+ services.nginx = {
+ enable = true;
+ virtualHosts.${domain} = {
+ addSSL = true;
+ enableACME = true;
+ locations."/" = {
+ proxyPass = "http://localhost:${toString port}";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/services/greetd.nix b/modules/services/greetd.mod.nix
similarity index 53%
rename from modules/services/greetd.nix
rename to modules/services/greetd.mod.nix
index ffec7f4..b55b815 100644
--- a/modules/services/greetd.nix
+++ b/modules/services/greetd.mod.nix
@@ -3,13 +3,17 @@
lib,
pkgs,
...
-}: let
- cfg = config.modules.services.greetd;
- uwsmEnabled = config.modules.services.uwsm.enable;
+}:
+let
+ inherit (lib.meta) getExe getExe';
+ inherit (lib.modules) mkIf;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.types) str listOf;
- inherit (lib.modules) mkIf;
-in {
+
+ inherit (config.meta.mainUser) username;
+ cfg = config.modules.services.greetd;
+in
+{
options.modules.services.greetd = {
enable = mkEnableOption "greetd";
greeter = mkOption {
@@ -23,26 +27,30 @@ in {
session = mkOption {
description = "Which login session to start";
type = str;
- default =
- if uwsmEnabled
- then "uwsm start Hyprland"
- else "Hyprland";
+ default = "niri";
};
};
config = mkIf cfg.enable {
- services.greetd = {
- enable = true;
- package = pkgs.greetd;
- settings.default_session = {
- command = ''
- ${pkgs.greetd.tuigreet}/bin/tuigreet \
- -c \"${cfg.session}\" \
- -r
- -t --time-format "DD.MM.YYYY"
- --asteriks'';
+ services.greetd =
+ let
+ session = {
+ command = ''
+ ${getExe pkgs.greetd.tuigreet} \
+ -c \"${cfg.session}\" \
+ -r
+ -t --time-format "DD.MM.YYYY"
+ --asteriks'';
+ user = "greeter";
+ };
+ in
+ {
+ enable = true;
+ vt = 7;
+ settings = {
+ default_session = session;
+ initial_session = session;
+ };
};
- vt = 7;
- };
};
}
diff --git a/modules/services/kanata/module.nix b/modules/services/kanata/kanata.mod.nix
similarity index 54%
rename from modules/services/kanata/module.nix
rename to modules/services/kanata/kanata.mod.nix
index b7164d5..d9a21a4 100644
--- a/modules/services/kanata/module.nix
+++ b/modules/services/kanata/kanata.mod.nix
@@ -2,24 +2,24 @@
config,
lib,
...
-}: let
+}:
+let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.modules.services.kanata;
-in {
+in
+{
options.modules.services.kanata.enable = mkEnableOption "kanata";
config = mkIf cfg.enable {
services.kanata = {
enable = true;
-
- keyboards.daskeyboard = {
- devices = ["/dev/input/by-id/usb-Metadot_-_Das_Keyboard_Das_Keyboard-event-kbd"];
- config = builtins.readFile (./. + "/main.kbd");
- };
-
- keyboards.laptop = {
- devices = ["/dev/input/by-path/platform-i8042-serio-0-event-kbd"];
+ keyboards.main = {
+ devices = [
+ "/dev/input/by-id/usb-Metadot_-_Das_Keyboard_Das_Keyboard-event-kbd"
+ "/dev/input/by-path/platform-i8042-serio-0-event-kbd"
+ "/dev/input/by-id/usb-Dell_Dell_USB_Keyboard-event-kbd"
+ ];
config = builtins.readFile (./. + "/main.kbd");
};
};
diff --git a/modules/services/locate.nix b/modules/services/locate.mod.nix
similarity index 85%
rename from modules/services/locate.nix
rename to modules/services/locate.mod.nix
index d945e6b..61bf0f4 100644
--- a/modules/services/locate.nix
+++ b/modules/services/locate.mod.nix
@@ -3,14 +3,16 @@
lib,
pkgs,
...
-}: let
+}:
+let
cfg = config.modules.services.locate;
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
-in {
+in
+{
options.modules.services.locate.enable = mkEnableOption "Locate service";
config = mkIf cfg.enable {
- environment.systemPackages = [pkgs.plocate];
+ environment.systemPackages = [ pkgs.plocate ];
services.locate = {
enable = true;
interval = "hourly";
diff --git a/modules/services/mako.nix b/modules/services/mako.mod.nix
similarity index 64%
rename from modules/services/mako.nix
rename to modules/services/mako.mod.nix
index 4bbdc0e..2df18f9 100644
--- a/modules/services/mako.nix
+++ b/modules/services/mako.mod.nix
@@ -1,8 +1,9 @@
-{pkgs, ...}: let
+{ pkgs, ... }:
+let
mako-wrapped = pkgs.symlinkJoin {
name = "mako-wrapped";
- paths = [pkgs.mako];
- buildInputs = [pkgs.makeWrapper];
+ paths = [ pkgs.mako ];
+ nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/mako --add-flags "\
--font 'Lexend 11' \
@@ -12,6 +13,7 @@
--default-timeout 4000"
'';
};
-in {
- environment.systemPackages = [mako-wrapped];
+in
+{
+ environment.systemPackages = [ mako-wrapped ];
}
diff --git a/modules/services/module.nix b/modules/services/module.nix
deleted file mode 100644
index b874528..0000000
--- a/modules/services/module.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-_: {
- imports = [
- ./pipewire.nix
- ./locate.nix
- ./ssh.nix
- ./greetd.nix
- ./mako.nix
- ./mpd.nix
- ./firewall.nix
- ];
-}
diff --git a/modules/services/monitoring/loki/loki.mod.nix b/modules/services/monitoring/loki/loki.mod.nix
new file mode 100644
index 0000000..4bdd589
--- /dev/null
+++ b/modules/services/monitoring/loki/loki.mod.nix
@@ -0,0 +1,27 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.modules) mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ cfg = config.modules.system.services.loki;
+ port = 4026;
+ dataDir = "/srv/data/loki";
+in
+{
+ options.modules.system.services.loki.enable = mkEnableOption "Grafana, a graphing service";
+
+ config = mkIf cfg.enable {
+ services.loki = {
+ enable = true;
+ package = pkgs.loki;
+
+ configuration = {
+ };
+ };
+ };
+}
diff --git a/modules/services/mpd.nix b/modules/services/mpd.mod.nix
similarity index 95%
rename from modules/services/mpd.nix
rename to modules/services/mpd.mod.nix
index 195af90..edf67bf 100644
--- a/modules/services/mpd.nix
+++ b/modules/services/mpd.mod.nix
@@ -3,13 +3,15 @@
lib,
pkgs,
...
-}: let
+}:
+let
cfg = config.modules.services.media.mpd;
inherit (config.meta.mainUser) username;
inherit (lib.modules) mkIf;
inherit (lib.types) str;
inherit (lib.options) mkOption mkEnableOption;
-in {
+in
+{
options.modules.services = {
media = {
mpd = {
@@ -24,7 +26,7 @@ in {
};
config = mkIf cfg.enable {
# command line interface to mpd
- environment.systemPackages = [pkgs.mpc];
+ environment.systemPackages = [ pkgs.mpc ];
systemd.services.mpd.environment = {
# https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/609
diff --git a/modules/services/nginx/module.nix b/modules/services/nginx.mod.nix
similarity index 94%
rename from modules/services/nginx/module.nix
rename to modules/services/nginx.mod.nix
index 67e958a..e407478 100644
--- a/modules/services/nginx/module.nix
+++ b/modules/services/nginx.mod.nix
@@ -3,11 +3,13 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.modules) mkIf mkDefault;
inherit (lib.options) mkEnableOption;
cfg = config.modules.system.services.nginx;
-in {
+in
+{
options.modules.system.services.nginx.enable = mkEnableOption "nginx";
config = mkIf cfg.enable {
security = {
@@ -25,7 +27,6 @@ in {
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
- recommendedZstdSettings = true;
# nginx defaults to a 1MB size limit for uploads, which
# *definitely* isn't enough for Git LFS.
diff --git a/modules/services/owncloud/module.nix b/modules/services/owncloud.mod.nix
similarity index 51%
rename from modules/services/owncloud/module.nix
rename to modules/services/owncloud.mod.nix
index 2d7974e..2be69c1 100644
--- a/modules/services/owncloud/module.nix
+++ b/modules/services/owncloud.mod.nix
@@ -3,12 +3,14 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.meta) getExe';
inherit (lib.modules) mkIf;
cfg = config.modules.system.services.owncloud;
-in {
+in
+{
options.modules.system.services.owncloud.enable = lib.mkEnableOption "owncloud";
config = {
@@ -16,33 +18,16 @@ in {
description = "Owncloud client service";
# makes the graphical session start this service when it starts
- wantedBy = ["graphical-session.target"];
+ wantedBy = [ "graphical-session.target" ];
# when graphical session restarts or gets stopped, this also gets restarted/stopped.
- partOf = ["graphical-session.target"];
+ partOf = [ "graphical-session.target" ];
# gets started only after graphical session
- after = ["graphical-session.target"];
+ after = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${getExe' pkgs.owncloud-client "owncloud"}";
Restart = "always";
RestartSec = 30;
-
- # User = "cr";
- # Group = "cr";
-
- Keyringmode = "shared";
- DevicePolicy = "closed";
- PrivateDevices = true;
- PrivateTmp = true;
- ProtectClock = true;
- ProtectControlGroups = true;
- ProtectControlGroup = true;
- ProtectKernelLogs = true;
- ProtectKernelModules = true;
- ProtectKernelTunables = true;
-
- ProtectSystem = "strict";
- SystemCallFilter = "~@clock @cpu-emulation @debug @obsolete @module @mount @raw-io @reboot @swap @privileged";
};
};
};
diff --git a/modules/services/pipewire.nix b/modules/services/pipewire.mod.nix
similarity index 96%
rename from modules/services/pipewire.nix
rename to modules/services/pipewire.mod.nix
index bb375c3..0f5e74f 100644
--- a/modules/services/pipewire.nix
+++ b/modules/services/pipewire.mod.nix
@@ -2,10 +2,12 @@
config,
lib,
...
-}: let
+}:
+let
cfg = config.modules.system.sound;
inherit (lib.modules) mkIf;
-in {
+in
+{
config = mkIf cfg.enable {
services.pulseaudio.enable = false;
services.pipewire = {
diff --git a/modules/services/postgresql/module.nix b/modules/services/postgresql.mod.nix
similarity index 89%
rename from modules/services/postgresql/module.nix
rename to modules/services/postgresql.mod.nix
index 84e95b5..363128d 100644
--- a/modules/services/postgresql/module.nix
+++ b/modules/services/postgresql.mod.nix
@@ -3,19 +3,21 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.modules.system.services.database.postgresql;
-in {
+in
+{
options.modules.system.services.database.postgresql.enable = mkEnableOption "postgresql";
config = mkIf cfg.enable {
services.postgresql = {
enable = true;
package = pkgs.postgresql_17;
- # NOTE: it is imperative to create this when starting postgresql!
+ # NOTE: it is imperative to create this manually when starting postgresql.
dataDir = "/srv/data/postgresql/${config.services.postgresql.package.psqlSchema}";
# Whether PostgreSQL should listen on all network interfaces.
@@ -27,6 +29,9 @@ in {
ensureDatabases = [
"git"
+ "grafana"
+ "stalwart"
+ "plausible"
];
ensureUsers = [
@@ -44,6 +49,18 @@ in {
name = "git";
ensureDBOwnership = true;
}
+ {
+ name = "grafana";
+ ensureDBOwnership = true;
+ }
+ {
+ name = "stalwart";
+ ensureDBOwnership = true;
+ }
+ {
+ name = "plausible";
+ ensureDBOwnership = true;
+ }
];
settings = {
# taken from https://pgconfigurator.cybertec.at/
diff --git a/modules/services/printing.mod.nix b/modules/services/printing.mod.nix
new file mode 100644
index 0000000..ea20bf8
--- /dev/null
+++ b/modules/services/printing.mod.nix
@@ -0,0 +1,58 @@
+{
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.options) mkEnableOption;
+in
+{
+ options.modules.services.cups.enable = mkEnableOption "CUPS, the Common UNIX printing system";
+
+ config = {
+ services = {
+ # enable CUPS, the Common UNIX printing system.
+ printing = {
+ enable = true;
+
+ # Move CUPSd's temporary directory.
+ # By default, it resides in /tmp.
+ tempDir = "/tmp/cups";
+
+ # Do not advertise shared printers.
+ browsing = true;
+
+ # browsedConf = ''
+ # BrowseDNSSDSubTypes _cups,_print
+ # BrowseLocalProtocols all
+ # BrowseRemoteProtocols all
+ # CreateIPPPrinterQueues All
+
+ # BrowseProtocols all
+ # '';
+
+ # Enable the CUPS webinterface, accessible at localhost:631.
+ webInterface = true;
+
+ logLevel = "debug";
+
+ # Some local or network printers might need additional drivers.
+ # These can be added into here.
+ drivers = with pkgs; [
+ gutenprint
+ cups-kyocera-ecosys-m552x-p502x
+ cups-filters
+ ];
+ };
+
+ # Add and autodiscover printers on the network via the IPP everywhere[1] protocol.
+ # [1] https://www.pwg.org/ipp/everywhere.html
+ avahi = {
+ enable = true;
+ nssmdns4 = true;
+ nssmdns6 = true;
+ openFirewall = true;
+ };
+ };
+ };
+}
diff --git a/modules/services/prometheus/prometheus.mod.nix b/modules/services/prometheus/prometheus.mod.nix
new file mode 100644
index 0000000..e5933d2
--- /dev/null
+++ b/modules/services/prometheus/prometheus.mod.nix
@@ -0,0 +1,73 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.modules) mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ cfg = config.modules.system.services.prometheus;
+ port = 4022;
+in
+{
+ options.modules.system.services.prometheus.enable = mkEnableOption "Grafana, a graphing service";
+
+ config = mkIf cfg.enable {
+ services.prometheus = {
+ enable = true;
+ package = pkgs.prometheus;
+ inherit port;
+
+ exporters = {
+ node = {
+ enable = true;
+ port = 4023;
+ enabledCollectors = [
+ "systemd"
+ "processes"
+ ];
+ };
+
+ postgres = {
+ enable = true;
+ port = 4024;
+ user = "postgres";
+ };
+ nginx = {
+ enable = true;
+ port = 4025;
+ };
+ };
+ scrapeConfigs = [
+ {
+ job_name = "prometheus";
+ scrape_interval = "30s";
+ static_configs = [ { targets = [ "localhost:${toString port}" ]; } ];
+ }
+ {
+ job_name = "node";
+ scrape_interval = "30s";
+ static_configs = [
+ { targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; }
+ ];
+ }
+ {
+ job_name = "postgres";
+ scrape_interval = "30s";
+ static_configs = [
+ { targets = [ "localhost:${toString config.services.prometheus.exporters.postgres.port}" ]; }
+ ];
+ }
+ {
+ job_name = "nginx";
+ scrape_interval = "30s";
+ static_configs = [
+ { targets = [ "localhost:${toString config.services.prometheus.exporters.nginx.port}" ]; }
+ ];
+ }
+ ];
+ };
+ };
+}
diff --git a/modules/services/searxng/module.nix b/modules/services/searxng/module.nix
deleted file mode 100644
index bfc0e5d..0000000
--- a/modules/services/searxng/module.nix
+++ /dev/null
@@ -1,119 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}: let
- inherit (lib.options) mkEnableOption;
- inherit (lib.modules) mkIf;
- inherit (builtins) toString;
- cfg = config.modules.services.searxng;
- port = 4021;
-in {
- options.modules.services.searxng.enable = mkEnableOption "SearXNG, a private search engine";
-
- config = mkIf cfg.enable {
- networking.firewall.allowedTCPPorts = [port];
-
- services = {
- nginx.enable = true;
- searx = {
- enable = true;
- package = pkgs.searxng;
- environmentFile = "/srv/data/searxng/super_secret_file";
- settings = {
- general = {
- name = "Copesearch";
- privacypolicy_url = false;
- debug = false;
- enable_metrics = false;
- };
-
- search = {
- safe_search = 1;
-
- formats = ["html" "json" "rss"];
- autocomplete = "google"; # "dbpedia", "duckduckgo", "google", "startpage", "swisscows", "qwant", "wikipedia" - leave blank to turn it off by default
- default_lang = "en";
- };
-
- ui = {
- query_in_title = true;
- theme_args.simple_style = "dark"; # auto, dark, light
- results_on_new_tab = false;
- };
-
- server = {
- inherit port;
- limiter = false;
- image_proxy = false;
-
- # taken from https://github.com/searx/searx/issues/715
- default_http_headers = {
- X-Content-Type-Options = "nosniff";
- X-XSS-Protection = "1; mode=block";
- X-Download-Options = "noopen";
- X-Robots-Tag = "noindex, nofollow";
- Referrer-Policy = "no-referrer";
- };
- };
-
- # shamelessly stolen from NotAShelf
- engines = [
- {
- name = "wikipedia";
- engine = "wikipedia";
- shortcut = "w";
- base_url = "https://wikipedia.org/";
- }
- {
- name = "duckduckgo";
- engine = "duckduckgo";
- shortcut = "ddg";
- }
- {
- name = "google";
- engine = "google";
- shortcut = "g";
- use_mobile_ui = false;
- }
- {
- name = "archwiki";
- engine = "archlinux";
- shortcut = "aw";
- }
- {
- name = "github";
- engine = "github";
- categories = "it";
- shortcut = "gh";
- }
- {
- name = "nixpkgs";
- shortcut = "nx";
- engine = "elasticsearch";
- categories = "dev,nix";
- base_url = "https://nixos-search-5886075189.us-east-1.bonsaisearch.net:443";
- index = "latest-31-nixos-unstable";
- query_type = "match";
- }
- ];
- };
- };
- nginx.virtualHosts."search.copeberg.org" = {
- locations."/".proxyPass = "http://127.0.0.1:${toString port}";
- extraConfig = ''
- access_log /dev/null;
- error_log /dev/null;
- proxy_connect_timeout 60s;
- proxy_send_timeout 60s;
- proxy_read_timeout 60s;
- '';
-
- quic = true;
- forceSSL = true;
- enableACME = true;
- };
- };
- };
-}
diff --git a/modules/services/spotifyd.mod.nix b/modules/services/spotifyd.mod.nix
new file mode 100644
index 0000000..6ffecc9
--- /dev/null
+++ b/modules/services/spotifyd.mod.nix
@@ -0,0 +1,18 @@
+{
+ config,
+ lib,
+ ...
+}:
+let
+ inherit (lib.modules) mkIf;
+ inherit (config.modules.system) isGraphical;
+in
+{
+ # TODO: setup
+ services.spotifyd = mkIf isGraphical {
+ enable = true;
+ settings = {
+ # backend = "pipe";
+ };
+ };
+}
diff --git a/modules/services/ssh.nix b/modules/services/ssh.mod.nix
similarity index 51%
rename from modules/services/ssh.nix
rename to modules/services/ssh.mod.nix
index 248d54b..b8f5315 100644
--- a/modules/services/ssh.nix
+++ b/modules/services/ssh.mod.nix
@@ -1,16 +1,13 @@
-{lib, ...}: let
+{ lib, ... }:
+let
inherit (lib.options) mkEnableOption;
-in {
+in
+{
options.modules.programs.ssh.enable = mkEnableOption "ssh";
config = {
- # set the ssh socket globally. This alows all applications and shells to use
- # the ssh-agent.
- environment.sessionVariables.SSH_AUTH_SOCK = "/run/user/1000/ssh-agent";
- programs.ssh.startAgent = true;
-
services.openssh = {
enable = true;
- ports = [22];
+ ports = [ 22 ];
settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
diff --git a/modules/services/stalwart.mod.nix b/modules/services/stalwart.mod.nix
new file mode 100644
index 0000000..93f8273
--- /dev/null
+++ b/modules/services/stalwart.mod.nix
@@ -0,0 +1,135 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.modules) mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ domain = "charlieroot.dev";
+
+ cfg = config.modules.system.services.stalwart;
+in
+{
+ options.modules.system.services.stalwart.enable = mkEnableOption "stalwart";
+ config = mkIf cfg.enable {
+ # create the stallwart user
+ users.users.stalwart = {
+ home = "/var/lib/stalwart-mail";
+ useDefaultShell = true;
+ group = "stalwart";
+ isSystemUser = true;
+ };
+ users.groups.stalwart = { };
+
+ services.stalwart-mail = {
+ enable = true;
+ package = pkgs.stalwart-mail;
+ openFirewall = true;
+ settings = {
+ email = {
+ # All incoming messages via SMTP or LMTP are automatically encrypted before they are written to disk,
+ # provided the user has uploaded their S/MIME certificate or OpenPGP public key.
+
+ encryption.enable = true;
+ };
+ server = {
+ # The default server hostname is utilized in SMTP EHLO commands,
+ # as well as included in message headers and reports.
+ hostname = domain;
+ tls = {
+ # Specifies whether the TLS encryption is available for the listener.
+ enable = true;
+
+ # Specifies whether the listener should use implicit or explicit TLS encryption.
+ # If set to false (the default), the listener will use explicit TLS encryption,
+ # which requires clients to initiate a STARTTLS command before upgrading the connection
+ # to an encrypted one. If set to true, the listener will use implicit TLS encryption,
+ # which requires the connection to be encrypted from the start.
+ implicit = true;
+ };
+
+ # Listeners are responsible for receiving incoming TCP connections.
+ listener = {
+ # Unencrypted SMTP connections are received on port 25 by default.
+ # This is the standard port for SMTP, and is used by mail servers to send email to each other.
+ smtp = {
+ protocol = "smtp";
+ bind = [ "[::]:25" ];
+ tls.implicit = true;
+ };
+ # SMTP submissions with implicit TLS are received on port 465 by default.
+ # This is the standard port for SMTP submissions with native implicit TLS,
+ # and is used by mail clients to send email to mail servers.
+
+ submissions = {
+ bind = [ "[::]:465" ];
+ protocol = "smtp";
+ tls.implicit = true;
+ };
+ imaps = {
+ bind = [ "[::]:993" ];
+ protocol = "imap";
+ tls.implicit = true;
+ };
+ management = {
+ bind = [ "127.0.0.1:8080" ];
+ protocol = "http";
+ tls.implicit = true;
+ };
+ };
+ lookup.default = {
+ hostname = "mail.${domain}";
+ inherit domain;
+ };
+ };
+ # storage = {
+ # data = "postgresql";
+ # blob = "postgresql";
+ # fts = "postgresql";
+ # lookup = "postgresql";
+ # full-text = {
+ # default-language = "en";
+ # };
+ # };
+ # store = {
+ # postgresql = {
+ # # Specifies the database type, set to "postgresql" for PostgreSQL.
+ # type = "postgresql";
+
+ # # The hostname or IP address of the PostgreSQL server.
+ # host = "localhost";
+
+ # # Port PostgreSQL runs on. Defaults to 5432.
+ # port = "5432";
+
+ # # Name of the database to connect to.
+ # # TODO: add this to PostgreSQL.
+ # database = "stalwart";
+
+ # # The username used for authentication with the PostgreSQL server.
+ # # TODO: add this to PostgreSQL.
+ # user = "stalwart";
+
+ # password = "";
+
+ # # Enable TLS
+ # tls.enable = true;
+ # };
+ # };
+ };
+ };
+ services.nginx = {
+ enable = true;
+ virtualHosts."mail.${domain}" = {
+ addSSL = true;
+ enableACME = true;
+ locations."/" = {
+ proxyPass = "http://localhost:${toString 8080}";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/services/usbguard/module.nix b/modules/services/usbguard.mod.nix
similarity index 84%
rename from modules/services/usbguard/module.nix
rename to modules/services/usbguard.mod.nix
index 9bdaa71..40e8353 100644
--- a/modules/services/usbguard/module.nix
+++ b/modules/services/usbguard.mod.nix
@@ -3,18 +3,23 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (config.meta.mainUser) username;
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.modules.services.usbguard;
-in {
+in
+{
options.modules.services.usbguard.enable = mkEnableOption "usbguard";
config = mkIf cfg.enable {
- environment.systemPackages = [pkgs.usbguard];
+ environment.systemPackages = [ pkgs.usbguard ];
services.usbguard = {
enable = true;
- IPCAllowedUsers = ["root" "${username}"];
+ IPCAllowedUsers = [
+ "root"
+ "${username}"
+ ];
presentDevicePolicy = "allow";
rules = ''
allow with-interface equals { 08:*:* }
diff --git a/modules/services/uwsm/module.nix b/modules/services/uwsm.mod.nix
similarity index 96%
rename from modules/services/uwsm/module.nix
rename to modules/services/uwsm.mod.nix
index 8a16d98..bc4bc7e 100644
--- a/modules/services/uwsm/module.nix
+++ b/modules/services/uwsm.mod.nix
@@ -3,11 +3,13 @@
lib,
pkgs,
...
-}: let
+}:
+let
cfg = config.modules.services.uwsm;
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption;
-in {
+in
+{
options.modules.services.uwsm.enable = mkEnableOption "uwsm";
config = mkIf cfg.enable {
programs.uwsm = {
diff --git a/modules/services/wayneko/module.nix b/modules/services/wayneko.mod.nix
similarity index 55%
rename from modules/services/wayneko/module.nix
rename to modules/services/wayneko.mod.nix
index d3385e5..2eaf564 100644
--- a/modules/services/wayneko/module.nix
+++ b/modules/services/wayneko.mod.nix
@@ -1,50 +1,75 @@
{
- config,
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.meta) getExe;
-in {
+
+ custom-wayneko = pkgs.wayneko.overrideAttrs {
+ src = pkgs.fetchFromGitea {
+ domain = "copeberg.org";
+ owner = "virt";
+ repo = "wayneko";
+ rev = "3ed4e4e1f847213e712fe22e0278ec62c4fa2cf2";
+ hash = "sha256-NxNrcQkx82SQ5GRqcJbbgM/Qg3GY8Whu5m5wI3zQi18=";
+ };
+
+ hash = "";
+ };
+in
+{
systemd.user.services.wayneko = {
description = "Wayneko, as a systemd service";
# makes the graphical session start this service when it starts
- wantedBy = ["graphical-session.target"];
+ wantedBy = [ "graphical-session.target" ];
# when graphical session restarts or gets stopped, this also gets restarted/stopped.
- partOf = ["graphical-session.target"];
+ partOf = [ "graphical-session.target" ];
# gets started only after graphical session
- after = ["graphical-session.target"];
+ after = [ "graphical-session.target" ];
serviceConfig = {
- ExecStart = "${getExe pkgs.wayneko} --layer top --follow-pointer false";
+ ExecStart = ''
+ ${getExe custom-wayneko}\
+ --layer top\
+ --type striped\
+ --background-colour 0xb4befe\
+ --outline-colour 0x1e1e2e
+ '';
Restart = "on-failure";
RestartSec = 1;
TimeoutStopSec = 10;
+ CapabilityBoundingSet = [ "" ];
+ DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
-
- PrivateTmp = true;
PrivateDevices = true;
- DevicePolicy = "closed";
PrivateNetwork = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+
ProtectClock = true;
ProtectControlGroups = true;
ProtectControlGroup = true;
+ ProtectHome = "read-only";
+ ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
+ ProtectProc = "invisible";
ProtectSystem = "strict";
- ProtectHome = "read-only";
-
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictRealTime = true;
RestrictSUIDSGID = true;
- SystemCallFilter = "~@clock @cpu-emulation @debug @obsolete @module @mount @raw-io @reboot @swap";
+ SystemCallArchitectures = [ "native" ];
+
+ SystemCallFilter = "~@clock @cpu-emulation @debug @obsolete @module @mount @raw-io @reboot @swap @privileged";
};
};
}
diff --git a/modules/style/colors.nix b/modules/style/colors.mod.nix
similarity index 76%
rename from modules/style/colors.nix
rename to modules/style/colors.mod.nix
index 07934ae..344788d 100644
--- a/modules/style/colors.nix
+++ b/modules/style/colors.mod.nix
@@ -9,10 +9,24 @@
config,
lib,
...
-}: let
+}:
+let
inherit (lib.options) mkOption literalExpression;
- inherit (lib.strings) toLower replaceStrings removePrefix hasPrefix isString;
- inherit (lib.types) str nullOr enum mkOptionType attrsOf coercedTo;
+ inherit (lib.strings)
+ toLower
+ replaceStrings
+ removePrefix
+ hasPrefix
+ isString
+ ;
+ inherit (lib.types)
+ str
+ nullOr
+ enum
+ mkOptionType
+ attrsOf
+ coercedTo
+ ;
cfg = config.modules.style;
@@ -24,18 +38,26 @@
};
colorType = attrsOf (coercedTo str (removePrefix "#") hexColorType);
- nameToSlug = name: toLower (replaceStrings [" "] ["-"] name);
- getPaletteFromScheme = slug:
- if builtins.pathExists ./palettes/${slug}.nix
- then (import ./palettes/${slug}.nix).colorscheme.palette
- else throw "The following colorscheme was imported but not found: ${slug}";
-in {
+ nameToSlug = name: toLower (replaceStrings [ " " ] [ "-" ] name);
+ getPaletteFromScheme =
+ slug:
+ if builtins.pathExists ./palettes/${slug}.nix then
+ (import ./palettes/${slug}.nix).colorscheme.palette
+ else
+ throw "The following colorscheme was imported but not found: ${slug}";
+in
+{
options.modules.style = {
colorScheme = {
name = mkOption {
- type = nullOr (enum ["Catppuccin Mocha" "Zenburn" "Black Metal Venom" "Gruvbox"]);
+ type = nullOr (enum [
+ "Catppuccin Mocha"
+ "Zenburn"
+ "Black Metal Venom"
+ "Gruvbox"
+ ]);
description = "The colorscheme that should be used globally to theme your system.";
- default = "Gruvbox";
+ default = "Catppuccin Mocha";
};
slug = mkOption {
@@ -83,11 +105,11 @@ in {
};
variant = mkOption {
- type = enum ["dark" "light"];
- default =
- if builtins.substring 0 1 cfg.colorScheme.colors.base00 < "5"
- then "dark"
- else "light";
+ type = enum [
+ "dark"
+ "light"
+ ];
+ default = if builtins.substring 0 1 cfg.colorScheme.colors.base00 < "5" then "dark" else "light";
description = ''
Whether the scheme is dark or light
'';
diff --git a/modules/style/default.nix b/modules/style/default.nix
deleted file mode 100644
index 8cb43d4..0000000
--- a/modules/style/default.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-_: {
- imports = [
- ./qt.nix
- ./gtk.nix
- ./module.nix
- ./fonts.nix
- ];
-}
diff --git a/modules/style/fonts.nix b/modules/style/fonts.mod.nix
similarity index 61%
rename from modules/style/fonts.nix
rename to modules/style/fonts.mod.nix
index 18727cb..dcde8b2 100644
--- a/modules/style/fonts.nix
+++ b/modules/style/fonts.mod.nix
@@ -1,9 +1,11 @@
-{pkgs, ...}: let
+{ pkgs, ... }:
+let
inherit (builtins) mapAttrs;
- valiosevka = pkgs.iosevka.override {
+
+ fancy-iosevka = pkgs.iosevka.override {
privateBuildPlan = {
- family = "valiosevka";
- spacing = "normal";
+ family = "fancy-iosevka";
+ spacing = "term";
serifs = "sans";
noCvSs = true;
exportGlyphNames = false;
@@ -16,10 +18,31 @@
f = "diagonal-tailed-crossbar-at-x-height";
};
};
+ weights.Regular = {
+ shape = 400;
+ menu = 400;
+ css = 400;
+ };
+ weights.Bold = {
+ shape = 700;
+ menu = 700;
+ css = 700;
+ };
+ widths.Condensed = {
+ shape = 500;
+ menu = 3;
+ css = "condensed";
+ };
+ widths.normal = {
+ shape = 600;
+ menu = 5;
+ css = "normal";
+ };
};
set = "Fancy";
};
-in {
+in
+{
# A (somewhat) sane list of fonts to be installed.
fonts = {
fontconfig = {
@@ -37,25 +60,26 @@ in {
# Set the defalt fonts. This was taken from raf,
# many thanks.
- defaultFonts = let
- common = [
- "Iosevka Nerd Font"
- "Roboto Mono Nerd Font"
- "Fira Code Nerd Font"
- "Symbols Nerd Font"
- "Noto Color Emoji"
- ];
- in
+ defaultFonts =
+ let
+ common = [
+ "Iosevka Nerd Font"
+ "Roboto Mono Nerd Font"
+ "Fira Code Nerd Font"
+ "Symbols Nerd Font"
+ "Noto Color Emoji"
+ "JetbrainsMono Nerd Font"
+ ];
+ in
mapAttrs (_: fonts: fonts ++ common) {
- serif = ["Noto Serif"];
- sansSerif = ["Lexend"];
- emoji = ["Noto Color Emoji"];
- monospace = ["Iosevka Nerd Font"];
+ serif = [ "Noto Serif" ];
+ sansSerif = [ "Lexend" ];
+ emoji = [ "Noto Color Emoji" ];
+ monospace = [ "Iosevka Nerd Font" ];
};
};
packages = builtins.attrValues {
- inherit
- (pkgs)
+ inherit (pkgs)
material-icons
material-design-icons
lexend
@@ -66,14 +90,14 @@ in {
corefonts
font-awesome
;
- inherit
- (pkgs.nerd-fonts)
+ inherit (pkgs.nerd-fonts)
iosevka
jetbrains-mono
comic-shanns-mono
symbols-only
;
- inherit valiosevka;
+
+ # inherit fancy-iosevka;
};
fontDir = {
# Whether to create a directory with links to all fonts in
diff --git a/modules/style/gtk-colors.nix b/modules/style/gtk-colors.nix
new file mode 100644
index 0000000..eed6ff9
--- /dev/null
+++ b/modules/style/gtk-colors.nix
@@ -0,0 +1,93 @@
+# blatantly stolen from sioodmy, thanks :3
+{ colors }:
+with colors;
+''
+ @define-color accent_color #${base0D};
+ @define-color accent_bg_color #${base0D};
+ @define-color accent_fg_color #${base00};
+ @define-color destructive_color #${base08};
+ @define-color destructive_bg_color #${base08};
+ @define-color destructive_fg_color #${base00};
+ @define-color success_color #${base0B};
+ @define-color success_bg_color #${base0B};
+ @define-color success_fg_color #${base00};
+ @define-color warning_color #${base0E};
+ @define-color warning_bg_color #${base0E};
+ @define-color warning_fg_color #${base00};
+ @define-color error_color #${base08};
+ @define-color error_bg_color #${base08};
+ @define-color error_fg_color #${base00};
+ @define-color window_bg_color #${base00};
+ @define-color window_fg_color #${base05};
+ @define-color view_bg_color #${base00};
+ @define-color view_fg_color #${base05};
+ @define-color headerbar_bg_color #${base01};
+ @define-color headerbar_fg_color #${base05};
+ @define-color headerbar_border_color #${base01};
+ @define-color headerbar_backdrop_color @window_bg_color;
+ @define-color headerbar_shade_color rgba(0, 0, 0, 0.07);
+ @define-color headerbar_darker_shade_color rgba(0, 0, 0, 0.07);
+ @define-color sidebar_bg_color #${base01};
+ @define-color sidebar_fg_color #${base05};
+ @define-color sidebar_backdrop_color @window_bg_color;
+ @define-color sidebar_shade_color rgba(0, 0, 0, 0.07);
+ @define-color secondary_sidebar_bg_color @sidebar_bg_color;
+ @define-color secondary_sidebar_fg_color @sidebar_fg_color;
+ @define-color secondary_sidebar_backdrop_color @sidebar_backdrop_color;
+ @define-color secondary_sidebar_shade_color @sidebar_shade_color;
+ @define-color card_bg_color #${base01};
+ @define-color card_fg_color #${base05};
+ @define-color card_shade_color rgba(0, 0, 0, 0.07);
+ @define-color dialog_bg_color #${base01};
+ @define-color dialog_fg_color #${base05};
+ @define-color popover_bg_color #${base01};
+ @define-color popover_fg_color #${base05};
+ @define-color popover_shade_color rgba(0, 0, 0, 0.07);
+ @define-color shade_color rgba(0, 0, 0, 0.07);
+ @define-color scrollbar_outline_color #${base02};
+ @define-color blue_1 #${base0D};
+ @define-color blue_2 #${base0D};
+ @define-color blue_3 #${base0D};
+ @define-color blue_4 #${base0D};
+ @define-color blue_5 #${base0D};
+ @define-color green_1 #${base0B};
+ @define-color green_2 #${base0B};
+ @define-color green_3 #${base0B};
+ @define-color green_4 #${base0B};
+ @define-color green_5 #${base0B};
+ @define-color yellow_1 #${base0A};
+ @define-color yellow_2 #${base0A};
+ @define-color yellow_3 #${base0A};
+ @define-color yellow_4 #${base0A};
+ @define-color yellow_5 #${base0A};
+ @define-color orange_1 #${base09};
+ @define-color orange_2 #${base09};
+ @define-color orange_3 #${base09};
+ @define-color orange_4 #${base09};
+ @define-color orange_5 #${base09};
+ @define-color red_1 #${base08};
+ @define-color red_2 #${base08};
+ @define-color red_3 #${base08};
+ @define-color red_4 #${base08};
+ @define-color red_5 #${base08};
+ @define-color purple_1 #${base0E};
+ @define-color purple_2 #${base0E};
+ @define-color purple_3 #${base0E};
+ @define-color purple_4 #${base0E};
+ @define-color purple_5 #${base0E};
+ @define-color brown_1 #${base0F};
+ @define-color brown_2 #${base0F};
+ @define-color brown_3 #${base0F};
+ @define-color brown_4 #${base0F};
+ @define-color brown_5 #${base0F};
+ @define-color light_1 #${base01};
+ @define-color light_2 #${base01};
+ @define-color light_3 #${base01};
+ @define-color light_4 #${base01};
+ @define-color light_5 #${base01};
+ @define-color dark_1 #${base01};
+ @define-color dark_2 #${base01};
+ @define-color dark_3 #${base01};
+ @define-color dark_4 #${base01};
+ @define-color dark_5 #${base01};
+''
diff --git a/modules/style/gtk.mod.nix b/modules/style/gtk.mod.nix
new file mode 100644
index 0000000..8be837b
--- /dev/null
+++ b/modules/style/gtk.mod.nix
@@ -0,0 +1,117 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (builtins) toString isBool;
+ inherit (lib.generators) toINI;
+ inherit (lib.modules) mkIf;
+ inherit (lib.options) mkOption mkEnableOption;
+ inherit (lib.strings) escape;
+ inherit (lib.trivial) boolToString;
+ inherit (lib.types) str package;
+
+ cfg = config.modules.theming.gtk;
+
+ toGtk3Ini = toINI {
+ mkKeyValue =
+ key: value:
+ let
+ value' = if isBool value then boolToString value else toString value;
+ in
+ "${escape [ "=" ] key}=${value'}";
+ };
+
+ gtkIni = {
+ gtk-application-prefer-dark-theme = 1;
+ gtk-font-name = "Lexend 11";
+ gtk-icon-theme-name = "Papirus";
+ gtk-xft-antialias = 1;
+ gtk-xft-hinting = 1;
+ gtk-xft-hintstyle = "hintslight";
+ gtk-xft-rgba = "rgb";
+ gtk-cursor-theme-name = "BreezeX-RosePine-Linux";
+ gtk-theme-name = "Gruvbox-Dark";
+ };
+in
+{
+ options.modules.theming.gtk = {
+ enable = mkEnableOption "Wether to enable GTK theming";
+ theme = {
+ name = mkOption {
+ description = "The GTK theme name";
+ default = "Gruvbox-Dark";
+ type = str;
+ };
+ package = mkOption {
+ description = "The GTK theme package";
+ default = pkgs.gruvbox-gtk-theme;
+ type = package;
+ };
+ };
+ iconTheme = {
+ description = "The GTK icon theme";
+ name = mkOption {
+ description = "The GTK icon theme name";
+ default = "Papirus";
+ type = str;
+ };
+ package = mkOption {
+ description = "The GTK icon theme package";
+ default = pkgs.papirus-icon-theme;
+ type = package;
+ };
+ };
+ };
+ config =
+ let
+ cursorSize = 32;
+ in
+ mkIf cfg.enable {
+ programs.dconf.enable = true;
+ environment = {
+ systemPackages = builtins.attrValues {
+ inherit (pkgs)
+ rose-pine-cursor
+ gruvbox-gtk-theme
+ papirus-icon-theme
+ colloid-icon-theme
+ ;
+ };
+ variables = {
+ GTK_THEME = cfg.theme.name;
+ XCURSOR_THEME = "BreezeX-RosePine-Linux";
+ XCURSOR_SIZE = cursorSize;
+ };
+ etc =
+ let
+ css = import ./gtk-colors.nix { inherit (config.modules.style.colorScheme) colors; };
+ in
+ {
+ "xdg/gtk-4.0/settings.ini".text = toGtk3Ini {
+ Settings = gtkIni;
+ };
+ "xdg/gtk-3.0/settings.ini".text = toGtk3Ini {
+ Settings = gtkIni;
+ };
+ "xdg/gtk-4.0/gtk.css".text = css;
+ "xdg/gtk-3.0/gtk.css".text = css;
+
+ "xdg/gtk-2.0/gtkrc".text = ''
+ gtk-cursor-theme-name = BreezeX-RosePine-Linux
+ gtk-cursor-theme-size = ${toString cursorSize}
+ gtk-theme-name = ${cfg.theme.name}
+ gtk-icon-theme-name = ${cfg.iconTheme.name}
+ gtk-font-name = Lexend 11
+ '';
+
+ "xdg/Xresources".text = ''
+ Xcursor.size: ${toString cursorSize}
+ Xcursor.theme: BreezeX-RosePine-Linux
+ '';
+ };
+ };
+ };
+}
diff --git a/modules/style/gtk.nix b/modules/style/gtk.nix
deleted file mode 100644
index 2e180bc..0000000
--- a/modules/style/gtk.nix
+++ /dev/null
@@ -1,65 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}: let
- inherit (lib.modules) mkIf;
- inherit (lib.options) mkOption mkEnableOption;
- inherit (lib.types) str package;
-
- cfg = config.modules.theming.gtk;
-in {
- options.modules.theming.gtk = {
- enable = mkEnableOption "Wether to enable GTK theming";
- theme = {
- name = mkOption {
- description = "The GTK theme name";
- default = "Gruvbox-Dark-BL";
- type = str;
- };
- package = mkOption {
- description = "The GTK theme package";
- default = pkgs.gruvbox-gtk-theme;
- type = package;
- };
- };
- iconTheme = {
- description = "The GTK icon theme";
- name = mkOption {
- description = "The GTK icon theme name";
- default = "Papirus-Dark";
- type = str;
- };
- package = mkOption {
- description = "The GTK icon theme package";
- default = pkgs.papirus-icon-theme;
- type = package;
- };
- };
- };
- config = mkIf cfg.enable {
- # NOTE: we need this or gtk breaks
- programs.dconf.enable = true;
-
- environment = {
- systemPackages = builtins.attrValues {
- inherit
- (pkgs)
- gruvbox-gtk-theme
- papirus-icon-theme
- ;
- };
- variables = let
- cursorSize = 32;
- in {
- GTK_THEME = "Gruvbox-Dark";
- XCURSOR_THEME = "BreezeX-RosePine-Linux";
- XCURSOR_SIZE = cursorSize;
-
- HYPRCURSOR_THEME = "BreezeX-RosePine-Linux";
- HYPRCURSOR_SIZE = cursorSize;
- };
- };
- };
-}
diff --git a/modules/style/module.nix b/modules/style/module.nix
deleted file mode 100644
index 71664a0..0000000
--- a/modules/style/module.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-_: {
- imports = [
- ./colors.nix
- ./fonts.nix
- ./gtk.nix
- ./theming.nix
- ];
-}
diff --git a/modules/style/palettes/catppuccin-mocha.nix b/modules/style/palettes/catppuccin-mocha.nix
index cca0510..6c2d23d 100644
--- a/modules/style/palettes/catppuccin-mocha.nix
+++ b/modules/style/palettes/catppuccin-mocha.nix
@@ -14,8 +14,8 @@
base07 = "#b4befe"; # lavender
base08 = "#f38ba8"; # red
base09 = "#fab387"; # peach
- base0A = "#a6e3a1"; # yellow
- base0B = "#94e2d5"; # green
+ base0A = "#f9e2af"; # yellow
+ base0B = "#a6e3a1"; # green
base0C = "#a6e3a1"; # teal
base0D = "#89b4fa"; # blue
base0E = "#cba6f7"; # mauve
diff --git a/modules/style/qt.nix b/modules/style/qt.mod.nix
similarity index 87%
rename from modules/style/qt.nix
rename to modules/style/qt.mod.nix
index 77413e4..76769ed 100644
--- a/modules/style/qt.nix
+++ b/modules/style/qt.mod.nix
@@ -3,13 +3,15 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.modules) mkIf;
inherit (lib.options) mkEnableOption mkOption;
inherit (lib.types) str package;
cfg = config.modules.theming.qt;
-in {
+in
+{
options.modules.theming.qt = {
enable = mkEnableOption "qt theming";
name = mkOption {
@@ -34,7 +36,10 @@ in {
};
};
- config =
- mkIf cfg.enable {
+ config = mkIf cfg.enable {
+ qt = {
+ enable = true;
+ platformTheme = "qt5ct";
};
+ };
}
diff --git a/modules/style/quickshell/module.nix b/modules/style/quickshell/module.nix
deleted file mode 100644
index be5fc94..0000000
--- a/modules/style/quickshell/module.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- config,
- lib,
- inputs,
- pkgs,
- ...
-}: let
- # inherit (inputs) quickshell;
- # inherit (lib.generators) toKeyValue;
-in {
- # environment.systemPackages = [
- # qt6.qtimageformats # amog
- # qt6.qt5compat # shader fx
- # (quickshell.packages.x86_64-linux.default.override {
- # withJemalloc = true;
- # withQtSvg = true;
- # withX11 = true;
- # withPipewire = true;
- # withPam = true;
- # withHyprland = true;
- # })
- # pamtester # lockscreen
- # grim
- # imagemagick # screenshot
- # ];
-}
diff --git a/modules/style/quickshell/quickshell.mod.nix b/modules/style/quickshell/quickshell.mod.nix
new file mode 100644
index 0000000..5ab2e37
--- /dev/null
+++ b/modules/style/quickshell/quickshell.mod.nix
@@ -0,0 +1,34 @@
+{
+ config,
+ lib,
+ pkgs,
+ sources,
+ ...
+}:
+let
+ inherit (lib.modules) mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ cfg = config.modules.theming.quickshell;
+in
+{
+ options.modules.theming.quickshell.enable = mkEnableOption "quickshell";
+
+ config = mkIf cfg.enable {
+ environment.systemPackages = with pkgs; [
+ (pkgs.callPackage (sources.quickshell + "/default.nix") { })
+ qt6.qtimageformats
+ qt6.qt5compat
+ qt6.qtmultimedia
+ qt6.qtdeclarative
+ qt6.qtsvg
+ qt6.qtwayland
+ qt6.qtbase
+ kdePackages.breeze
+ kdePackages.breeze-icons
+ ];
+
+ # taken from outfoxxed.
+ qt.enable = true;
+ };
+}
diff --git a/modules/style/quickshell/shell/.qmlls.ini b/modules/style/quickshell/shell/.qmlls.ini
new file mode 120000
index 0000000..a780449
--- /dev/null
+++ b/modules/style/quickshell/shell/.qmlls.ini
@@ -0,0 +1 @@
+/run/user/1000/quickshell/vfs/4f7a8066a49ba487f5b2754750896151/.qmlls.ini
\ No newline at end of file
diff --git a/modules/style/quickshell/shell/AudioPopup.qml b/modules/style/quickshell/shell/AudioPopup.qml
new file mode 100644
index 0000000..383a9ef
--- /dev/null
+++ b/modules/style/quickshell/shell/AudioPopup.qml
@@ -0,0 +1,105 @@
+pragma ComponentBehavior: Bound
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import QtQuick.Layouts
+import Quickshell.Services.Pipewire
+import Quickshell.Wayland
+
+import qs.config
+
+Singleton {
+ id: root
+
+ property real volume: Pipewire.defaultAudioSink?.audio?.volume ?? 0
+ property real popupOpacity: 0
+
+ PwObjectTracker {
+ objects: [Pipewire.defaultAudioSink]
+ }
+
+ onVolumeChanged: {
+ root.popupOpacity = 1;
+ loader.activeAsync = true;
+ timer.restart();
+ timer_opacity.restart();
+ }
+
+ LazyLoader {
+ id: loader
+ activeAsync: false
+
+ PanelWindow {
+ id: popup
+ visible: true
+ implicitWidth: rect.implicitWidth
+ implicitHeight: 50
+ color: "transparent"
+ mask: Region {}
+ exclusionMode: ExclusionMode.Ignore
+ WlrLayershell.layer: WlrLayer.Overlay
+
+ anchors.bottom: true
+ margins.bottom: 100
+
+ Rectangle {
+ id: rect
+ Layout.fillWidth: true
+ anchors.fill: parent
+ color: Colors.surface0
+ implicitWidth: 300
+ implicitHeight: parent.implicitHeight
+ radius: 5
+ opacity: root.popupOpacity
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 500
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ Rectangle {
+ id: bar
+ color: Colors.blue
+ implicitWidth: rect.implicitWidth * root.volume - 20
+ implicitHeight: 30
+ topRightRadius: 5
+ bottomRightRadius: 5
+
+ anchors {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ }
+
+ Behavior on implicitWidth {
+ NumberAnimation {
+ duration: 300
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Timer {
+ id: timer
+ interval: 2000
+ running: false
+ onTriggered: loader.activeAsync = false
+ }
+
+ Timer {
+ id: timer_opacity
+ interval: 1500
+ running: false
+ onTriggered: {
+ root.popupOpacity = 0;
+ }
+ }
+
+ function init() {
+ }
+}
diff --git a/modules/style/quickshell/shell/ClockWidget.qml b/modules/style/quickshell/shell/ClockWidget.qml
new file mode 100644
index 0000000..6cf0ad7
--- /dev/null
+++ b/modules/style/quickshell/shell/ClockWidget.qml
@@ -0,0 +1,46 @@
+import QtQuick
+import Quickshell
+
+Rectangle {
+
+
+ width: text.width
+ implicitHeight: text.height
+ // border.color: "black"
+ border.width: 2
+ radius: 0
+ color: "transparent"
+
+ Behavior on implicitHeight {
+ NumberAnimation {
+ duration: 100
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ Item {
+ width: parent.width
+ height: text.height
+
+ anchors.centerIn: parent
+
+ SystemClock {
+ id: clock
+ precision: SystemClock.Seconds
+ }
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ property var date: Date()
+
+ text: Qt.formatDateTime(clock.date, "hh mm")
+
+ font.family: "ComicShannsMono Nerd Font Mono"
+ font.weight: Font.ExtraBold
+ font.pointSize: 12
+
+ color: "black"
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/Launcher.qml b/modules/style/quickshell/shell/Launcher.qml
new file mode 100644
index 0000000..0cfebfb
--- /dev/null
+++ b/modules/style/quickshell/shell/Launcher.qml
@@ -0,0 +1,310 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Io
+import Quickshell.Wayland
+import Quickshell.Widgets
+
+import qs.config
+
+Singleton {
+ id: launcher
+ property bool launcherOpen: false
+
+ IpcHandler {
+ target: "launcher"
+
+ function open(): void {
+ launcher.launcherOpen = true;
+ }
+
+ function close(): void {
+ launcher.launcherOpen = false;
+ }
+
+ function toggle(): void {
+ launcher.launcherOpen = !launcher.launcherOpen;
+ }
+ }
+
+ LazyLoader {
+ id: loader
+ activeAsync: launcher.launcherOpen
+
+ PanelWindow {
+ implicitWidth: 450
+ implicitHeight: 7 + searchContainer.implicitHeight + list.topMargin * 2 + list.delegateHeight * 10
+ color: "transparent"
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+
+ Rectangle {
+
+ height: 7 + searchContainer.implicitHeight + list.topMargin + list.bottomMargin + Math.min(list.contentHeight, list.delegateHeight * 10)
+ Behavior on height {
+ NumberAnimation {
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ }
+ width: 450
+ color: Colors.base
+ radius: 5
+ border.color: Colors.mantle
+ border.width: 2
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 7
+ anchors.bottomMargin: 0
+ spacing: 0
+
+ Rectangle {
+ id: searchContainer
+ Layout.fillWidth: true
+ implicitHeight: searchbox.implicitHeight + 10
+ color: Colors.base
+ radius: 3
+ border.color: Colors.mantle
+
+ RowLayout {
+ id: searchbox
+ anchors.fill: parent
+ anchors.margins: 5
+
+ TextInput {
+ id: search
+ Layout.fillWidth: true
+ color: Colors.text
+ font.pointSize: 13
+
+ focus: true
+ Keys.forwardTo: [list]
+ Keys.onEscapePressed: launcher.launcherOpen = false
+
+ Keys.onPressed: event => {
+ if (event.modifiers & Qt.ControlModifier) {
+ if (event.key == Qt.Key_J) {
+ list.currentIndex = list.currentIndex == list.count - 1 ? 0 : list.currentIndex + 1;
+ event.accepted = true;
+ } else if (event.key == Qt.Key_K) {
+ list.currentIndex = list.currentIndex == 0 ? list.count - 1 : list.currentIndex - 1;
+ event.accepted = true;
+ }
+ }
+ }
+
+ onAccepted: {
+ if (list.currentItem) {
+ list.currentItem.clicked(null);
+ }
+ }
+
+ onTextChanged: {
+ list.currentIndex = 0;
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: list
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ clip: true
+ cacheBuffer: 0 // works around QTBUG-131106
+ //reuseItems: true
+ model: ScriptModel {
+ values: DesktopEntries.applications.values.map(object => {
+ const stxt = search.text.toLowerCase();
+ const ntxt = object.name.toLowerCase();
+ let si = 0;
+ let ni = 0;
+
+ let matches = [];
+ let startMatch = -1;
+
+ for (let si = 0; si != stxt.length; ++si) {
+ const sc = stxt[si];
+
+ while (true) {
+ // Drop any entries with letters that don't exist in order
+ if (ni == ntxt.length)
+ return null;
+
+ const nc = ntxt[ni++];
+
+ if (nc == sc) {
+ if (startMatch == -1)
+ startMatch = ni;
+ break;
+ } else {
+ if (startMatch != -1) {
+ matches.push({
+ index: startMatch,
+ length: ni - startMatch
+ });
+
+ startMatch = -1;
+ }
+ }
+ }
+ }
+
+ if (startMatch != -1) {
+ matches.push({
+ index: startMatch,
+ length: ni - startMatch + 1
+ });
+ }
+
+ return {
+ object: object,
+ matches: matches
+ };
+ }).filter(entry => entry !== null).sort((a, b) => {
+ let ai = 0;
+ let bi = 0;
+ let s = 0;
+
+ while (ai != a.matches.length && bi != b.matches.length) {
+ const am = a.matches[ai];
+ const bm = b.matches[bi];
+
+ s = bm.length - am.length;
+ if (s != 0)
+ return s;
+
+ s = am.index - bm.index;
+ if (s != 0)
+ return s;
+
+ ++ai;
+ ++bi;
+ }
+
+ s = a.matches.length - b.matches.length;
+ if (s != 0)
+ return s;
+
+ s = a.object.name.length - b.object.name.length;
+ if (s != 0)
+ return s;
+
+ return a.object.name.localeCompare(b.object.name);
+ }).map(entry => entry.object)
+
+ onValuesChanged: list.currentIndex = 0
+ }
+
+ topMargin: 7
+ bottomMargin: list.count == 0 ? 0 : 7
+
+ add: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: 100
+ }
+ }
+
+ displaced: Transition {
+ NumberAnimation {
+ property: "y"
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ property: "opacity"
+ to: 1
+ duration: 100
+ }
+ }
+
+ move: Transition {
+ NumberAnimation {
+ property: "y"
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ property: "opacity"
+ to: 1
+ duration: 100
+ }
+ }
+
+ remove: Transition {
+ NumberAnimation {
+ property: "y"
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ property: "opacity"
+ to: 0
+ duration: 100
+ }
+ }
+
+ highlight: Rectangle {
+ radius: 5
+ color: "transparent"
+ border.color: Colors.lavender
+ border.width: 2
+ }
+ keyNavigationEnabled: true
+ keyNavigationWraps: true
+ highlightMoveVelocity: -1
+ highlightMoveDuration: 50
+ preferredHighlightBegin: list.topMargin
+ preferredHighlightEnd: list.height - list.bottomMargin
+ highlightRangeMode: ListView.ApplyRange
+ snapMode: ListView.SnapToItem
+
+ readonly property real delegateHeight: 44
+
+ delegate: MouseArea {
+ required property DesktopEntry modelData
+
+ implicitHeight: list.delegateHeight
+ implicitWidth: ListView.view.width
+
+ onClicked: {
+ modelData.execute();
+ launcher.launcherOpen = false;
+ }
+
+ RowLayout {
+ id: delegateLayout
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: 5
+ }
+
+ IconImage {
+ Layout.alignment: Qt.AlignVCenter
+ asynchronous: true
+ implicitSize: 30
+ source: Quickshell.iconPath(modelData.icon)
+ }
+ Text {
+ text: modelData.name
+ color: Colors.text
+ font.family: "JetBrainsMono Nerd Font Mono"
+ font.pointSize: 13
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ function init() {
+ }
+}
diff --git a/modules/style/quickshell/shell/ReloadPopup.qml b/modules/style/quickshell/shell/ReloadPopup.qml
new file mode 100644
index 0000000..513f814
--- /dev/null
+++ b/modules/style/quickshell/shell/ReloadPopup.qml
@@ -0,0 +1,127 @@
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+
+Scope {
+ id: root
+ property bool failed
+ property string errorString
+
+ // Connect to the Quickshell global to listen for the reload signals.
+ Connections {
+ target: Quickshell
+
+ function onReloadCompleted() {
+ root.failed = false;
+ popupLoader.loading = true;
+ }
+
+ function onReloadFailed(error: string) {
+ // Close any existing popup before making a new one.
+ popupLoader.active = false;
+
+ root.failed = true;
+ root.errorString = error;
+ popupLoader.loading = true;
+ }
+ }
+
+ // Keep the popup in a loader because it isn't needed most of the timeand will take up
+ // memory that could be used for something else.
+ LazyLoader {
+ id: popupLoader
+
+ PanelWindow {
+ id: popup
+
+ anchors {
+ top: true
+ right: true
+ }
+
+ margins {
+ top: 25
+ left: 25
+ }
+
+ width: rect.width
+ height: rect.height
+
+ // color blending is a bit odd as detailed in the type reference.
+ color: "transparent"
+
+ Rectangle {
+ id: rect
+ color: root.failed ? "#40802020" : "#40009020"
+
+ implicitHeight: layout.implicitHeight + 50
+ implicitWidth: layout.implicitWidth + 30
+
+ // Fills the whole area of the rectangle, making any clicks go to it,
+ // which dismiss the popup.
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: popupLoader.active = false
+
+ // makes the mouse area track mouse hovering, so the hide animation
+ // can be paused when hovering.
+ hoverEnabled: true
+ }
+
+ ColumnLayout {
+ id: layout
+ anchors {
+ top: parent.top
+ topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+
+ Text {
+ text: root.failed ? "Reloaing failed." : "Reloading completed!"
+ color: "white"
+ }
+
+ Text {
+ text: root.errorString
+ color: "white"
+ // When visible is false, it also takes up no space.
+ visible: root.errorString != ""
+ }
+ }
+
+ // A progress bar on the bottom of the screen, showing how long until the
+ // popup is removed.
+ Rectangle {
+ id: bar
+ color: "#20ffffff"
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ height: 20
+
+ PropertyAnimation {
+ id: anim
+ target: bar
+ property: "width"
+ from: rect.width
+ to: 0
+ duration: root.failed ? 10000 : 800
+ onFinished: popupLoader.active = false
+
+ // Pause the animation when the mouse is hovering over the popup,
+ // so it stays onscreen while reading. This updates reactively
+ // when the mouse moves on and off the popup.
+ paused: mouseArea.containsMouse
+ }
+ }
+
+ // We could set `running: true` inside the animation, but the width of the
+ // rectangle might not be calculated yet, due to the layout.
+ // In the `Component.onCompleted` event handler, all of the component's
+ // properties and children have been initialized.
+ Component.onCompleted: anim.start()
+ }
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/SysTray.qml b/modules/style/quickshell/shell/SysTray.qml
new file mode 100644
index 0000000..7445bcd
--- /dev/null
+++ b/modules/style/quickshell/shell/SysTray.qml
@@ -0,0 +1,57 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Effects
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Services.SystemTray
+
+Rectangle {
+ id: root
+ required property var bar
+
+ width: parent.width
+ height: column.height + 10
+ color: "#30c0ffff"
+ radius: 5
+ border.color: "black"
+ border.width: 2
+
+ RowLayout {
+ id: column
+ spacing: 10
+
+ anchors.centerIn: parent
+
+ Repeater {
+ model: SystemTray.items
+
+ Item {
+ id: item
+
+ required property SystemTrayItem modelData
+
+ height: 35
+ width: 35
+
+ Image {
+ source: item.modelData.icon
+ anchors.fill: parent
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: function (mouse) {
+ if (mouse.button === Qt.LeftButton) {
+ item.modelData.activate();
+ }
+ if (mouse.button === Qt.RightButton) {
+ if (item.modelData.hasMenu) {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/config/Colors.qml b/modules/style/quickshell/shell/config/Colors.qml
new file mode 100644
index 0000000..8f77553
--- /dev/null
+++ b/modules/style/quickshell/shell/config/Colors.qml
@@ -0,0 +1,25 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+
+Singleton {
+ id: root
+
+ readonly property color base: "#1e1e2e"
+ readonly property color mantle: "#181825"
+ readonly property color surface0: "#313244"
+ readonly property color surface1: "#45475a"
+ readonly property color surface2: "#585b70"
+ readonly property color text: "#cdd6f4"
+ readonly property color rosewater: "#f5e0dc"
+ readonly property color lavender: "#b4befe"
+ readonly property color red: "#f38ba8"
+ readonly property color peach: "#fab387"
+ readonly property color yellow: "#f9e2af"
+ readonly property color green: "#a6e3a1"
+ readonly property color teal: "#a6e3a1"
+ readonly property color blue: "#89b4fa"
+ readonly property color mauve: "#cba6f7"
+ readonly property color flamingo: "#f2cdcd"
+}
diff --git a/modules/style/quickshell/shell/config/Config.qml b/modules/style/quickshell/shell/config/Config.qml
new file mode 100644
index 0000000..9e44e32
--- /dev/null
+++ b/modules/style/quickshell/shell/config/Config.qml
@@ -0,0 +1,32 @@
+pragma Singleton
+
+import Quickshell
+import QtQuick
+
+Singleton {
+ id: root
+
+ readonly property QtObject bar: QtObject {
+ readonly property int width: 50
+ readonly property var colors: QtObject {
+ readonly property color bar: "#1e1e2e"
+ readonly property color barOutline: "#50ffffff"
+ readonly property color widget: "#25ceffff"
+ readonly property color widgetActive: "#80ceffff"
+ readonly property color widgetOutline: "#40ffffff"
+ readonly property color widgetOutlineSeparate: "#20ffffff"
+ readonly property color separator: "#60ffffff"
+ }
+ }
+
+ readonly property QtObject border: QtObject {
+ readonly property int thickness: 0
+ readonly property color color: "#1e1e2e"
+ readonly property int rounding: 0
+ }
+
+ readonly property QtObject volumeslider: QtObject {
+ readonly property int width: 50
+ }
+
+}
diff --git a/modules/style/quickshell/shell/modules/BackgroundImage.qml b/modules/style/quickshell/shell/modules/BackgroundImage.qml
new file mode 100644
index 0000000..9cf0ded
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/BackgroundImage.qml
@@ -0,0 +1,55 @@
+import QtQuick
+import Quickshell
+import Quickshell.Wayland
+import Qt.labs.folderlistmodel 2.9
+
+PanelWindow {
+ id: root
+
+ exclusionMode: ExclusionMode.Ignore
+ WlrLayershell.layer: WlrLayer.Background
+ WlrLayershell.namespace: "shell:background"
+
+ property string basePath: "file:///home/cr/Documents/Backgrounds/"
+ property var absPath: folderModel.get(Math.floor(Math.random() * folderModel.count), "filePath")
+ property var finalPath: absPath
+
+ // property bool _: log()
+ // function log() {
+ // console.log(absPath);
+ // console.log(folderModel.count);
+ // return true;
+ // }
+ FolderListModel {
+ id: folderModel
+ folder: root.basePath
+ nameFilters: ["*.png"]
+ showDirs: false
+ showFiles: true
+ }
+
+ anchors {
+ top: true
+ bottom: true
+ left: true
+ right: true
+ }
+
+ Item {
+ id: background
+ anchors.fill: parent
+ Image {
+ id: image
+ source: Qt.resolvedUrl(root.finalPath)
+ }
+ }
+ Timer {
+ id: timer
+ // 10 minutes
+ interval: 1000 * 60 * 10
+ running: false
+ onTriggered: {
+ root.popupOpacity = 0;
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/bar/Bar.qml b/modules/style/quickshell/shell/modules/bar/Bar.qml
new file mode 100644
index 0000000..a7b28d0
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/bar/Bar.qml
@@ -0,0 +1,50 @@
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+
+import qs.config
+import "components"
+
+Item {
+ id: root
+
+ required property ShellScreen screen
+
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ left: parent.left
+ }
+
+ implicitWidth: Config.bar.width
+
+ Item {
+ id: child
+
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
+
+ implicitWidth: Math.max(clock.implicitWidth, workspaces.implicitWidth)
+
+ ColumnLayout {
+ spacing: 2
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ margins: 0
+ }
+ Clock {
+ id: clock
+ }
+ Workspaces {
+ id: workspaces
+ screen: root.screen
+ }
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/bar/components/Clock.qml b/modules/style/quickshell/shell/modules/bar/components/Clock.qml
new file mode 100644
index 0000000..78d8c30
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/bar/components/Clock.qml
@@ -0,0 +1,34 @@
+import QtQuick
+import Quickshell
+
+import qs.config
+
+Rectangle {
+ id: root
+
+ width: text.width + 5
+ height: text.height + 5
+ implicitWidth: width
+ border.color: Colors.rosewater
+ border.width: 0
+ radius: 5
+ color: "transparent"
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ property var date: Date()
+
+ text: Qt.formatDateTime(clock.date, "hh mm")
+
+ font.family: "JetBrainsMono NF Mono"
+ font.pointSize: 15
+
+ color: Colors.text
+ }
+
+ SystemClock {
+ id: clock
+ precision: SystemClock.Seconds
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/bar/components/Workspaces.qml b/modules/style/quickshell/shell/modules/bar/components/Workspaces.qml
new file mode 100644
index 0000000..404f13e
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/bar/components/Workspaces.qml
@@ -0,0 +1,108 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import Quickshell
+import QtQuick.Layouts
+
+import qs.services.niri
+import qs.config
+
+Rectangle {
+ id: root
+
+ required property ShellScreen screen
+ property var workspaces: Niri.workspaces
+ property var wsCount: Niri.workspaces.length
+ property var activeWorkspace: Niri.activeWorkspace
+ property var activeWorkspaceIndex: Niri.activeWorkspaceIndex
+
+ property int wsItemHeight: 15
+
+ signal workspaceAdded(workspace: var)
+ function onWorkspaceAdded(workspace: var) {
+ root.workspaces.push(workspace);
+ }
+
+ // property bool _: log()
+ // function log() {
+ // console.log(workspaces.values);
+ // return true;
+ // }
+
+ // Works
+ height: 300
+
+ // Gives warning
+ // height: workspaces.length * root.wsItemHeight
+ implicitWidth: list.implicitWidth
+ color: "transparent"
+ border.color: Colors.rosewater
+ border.width: 0
+ radius: 7
+
+ Layout.fillWidth: true
+
+ ListView {
+ id: list
+ model: root.workspaces
+ implicitHeight: contentHeight
+ implicitWidth: contentItem.childrenRect.width
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ // anchors.fill: parent
+
+ delegate: Item {
+ id: wsItem
+ // Name of the workspace
+ property string name: "VOID"
+ // ID of the workspace
+ required property string id
+
+ required property string output
+
+ property bool isActive: (id - 1 == root.activeWorkspaceIndex)
+
+ property real animActive: isActive ? 1 : 0.65
+ Behavior on animActive {
+ NumberAnimation {
+ duration: 150
+ }
+ }
+
+ // property bool isCorrectScreen: log()
+ // function log() {
+ // console.log("Screen name: " + root.screen.name);
+ // console.log(wsItem.output);
+ // console.log(wsItem.id);
+
+ // let isCorrect = root.screen.name == wsItem.output;
+ // console.log("isCorrect: ", isCorrect);
+ // return root.screen.name == wsItem.output;
+ // }
+
+ implicitHeight: root.wsItemHeight
+ implicitWidth: 50
+
+ anchors {
+ right: parent.right
+ left: parent.left
+ }
+
+ Rectangle {
+ anchors.centerIn: parent
+ height: wsItem.height - 5
+ width: parent.width * wsItem.animActive
+ radius: height / 2
+ border.color: Colors.mantle
+ border.width: 0
+ color: Colors.blue
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ Niri.workspaces.forEach(workspace => {
+ root.workspaceAdded(workspace);
+ });
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/drawers/Backgrounds.qml b/modules/style/quickshell/shell/modules/drawers/Backgrounds.qml
new file mode 100644
index 0000000..7e50ae9
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/drawers/Backgrounds.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+
+Rectangle {
+ required property Item bar
+}
diff --git a/modules/style/quickshell/shell/modules/drawers/Border.qml b/modules/style/quickshell/shell/modules/drawers/Border.qml
new file mode 100644
index 0000000..6fd6538
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/drawers/Border.qml
@@ -0,0 +1,54 @@
+import QtQuick
+import QtQuick.Effects
+
+import qs.config
+
+Item {
+ id: root
+
+ required property Item bar
+
+ anchors.fill: parent
+
+ Rectangle {
+ id: rect
+
+ // Parent has a mask applied that cuts out all except the border and the bar.
+ anchors.fill: parent
+ color: Config.border.color
+ visible: false
+
+
+ Behavior on color {
+ ColorAnimation {
+ duration: 150
+ easing.type: Easing.BezierSpline
+ }
+ }
+ }
+
+ Item {
+ id: mask
+
+ anchors.fill: parent
+ layer.enabled: true
+ visible: false
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: Config.border.thickness
+ anchors.leftMargin: root.bar.implicitWidth
+ radius: Config.border.rounding
+ }
+ }
+
+ MultiEffect {
+ anchors.fill: parent
+ maskEnabled: true
+ maskInverted: true
+ maskSource: mask
+ source: rect
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 1
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/drawers/Drawers.qml b/modules/style/quickshell/shell/modules/drawers/Drawers.qml
new file mode 100644
index 0000000..466182c
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/drawers/Drawers.qml
@@ -0,0 +1,100 @@
+pragma ComponentBehavior: Bound
+
+import Quickshell
+import Quickshell.Wayland
+import QtQuick
+import QtQuick.Effects
+
+import qs.modules.bar
+
+import qs.config
+import qs.modules
+
+Variants {
+ model: Quickshell.screens
+
+ Scope {
+ id: scope
+ required property ShellScreen modelData
+
+ Exclusions {
+ screen: scope.modelData
+ bar: bar
+ }
+
+ PanelWindow {
+ id: win
+
+ screen: scope.modelData
+ color: "transparent"
+
+ WlrLayershell.exclusionMode: ExclusionMode.Ignore
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
+
+ // Clickthrough mask.
+ // Clickable areas of the window are determined by the provided region.
+ mask: Region {
+ // Start at the bottom left; right of the bar and on top of the border
+ x: bar.implicitWidth
+ y: Config.border.thickness
+
+ // Width is the window width - the bar's width - the border thickness
+ width: win.width - bar.implicitWidth - Config.border.thickness
+
+ // Height is window width - the border thickness x2 —top border and bottom border.
+ height: win.height - Config.border.thickness * 2
+
+ // Setting the intersection mode to Xor will invert the mask and make everything in the mask region not clickable and pass through clicks inside it through the window.
+ intersection: Intersection.Xor
+ // Region {
+ // item: volume
+ // intersection: Intersection.Subtract
+ // }
+ }
+
+ anchors {
+ top: true
+ bottom: true
+ left: true
+ right: true
+ }
+
+ Item {
+ id: background
+
+ anchors.fill: parent
+ visible: false
+
+ Border {
+ bar: bar
+ }
+
+ Backgrounds {
+ bar: bar
+ }
+ }
+
+ MultiEffect {
+ anchors.fill: source
+ source: background
+ shadowEnabled: true
+ blurMax: 15
+ }
+
+ Bar {
+ id: bar
+ screen: scope.modelData
+ }
+ }
+ BackgroundImage {
+ id: backgroundimage
+ screen: scope.modelData
+ }
+ BackgroundImage {
+ id: background_overview
+ screen: scope.modelData
+
+ WlrLayershell.namespace: "shell:background-overview"
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/drawers/Exclusions.qml b/modules/style/quickshell/shell/modules/drawers/Exclusions.qml
new file mode 100644
index 0000000..d99092d
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/drawers/Exclusions.qml
@@ -0,0 +1,38 @@
+
+pragma ComponentBehavior: Bound
+import Quickshell
+import QtQuick
+
+import "../../config"
+
+Scope {
+ id: root
+ required property ShellScreen screen
+ required property Item bar
+
+ ExclusionZone {
+ anchors.left: true
+ exclusiveZone: root.bar.implicitWidth
+ }
+
+ ExclusionZone {
+ anchors.top: true
+ }
+
+ ExclusionZone {
+ anchors.right: true
+ }
+
+ ExclusionZone {
+ anchors.bottom: true
+ }
+
+ component ExclusionZone: PanelWindow {
+ screen: root.screen
+ color: "transparent"
+ exclusiveZone: Config.border.thickness
+ implicitHeight: Config.border.thickness
+ implicitWidth: Config.border.thickness
+ mask: Region {}
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/launcher/Launcher.qml b/modules/style/quickshell/shell/modules/launcher/Launcher.qml
new file mode 100644
index 0000000..8cf4f37
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/launcher/Launcher.qml
@@ -0,0 +1,319 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Io
+import Quickshell.Wayland
+import Quickshell.Widgets
+
+Singleton {
+ id: launcher
+ property bool launcherOpen: false
+
+ IpcHandler {
+ target: "launcher"
+
+ function open(): void {
+ launcher.launcherOpen = true;
+ }
+
+ function close(): void {
+ launcher.launcherOpen = false;
+ }
+
+ function toggle(): void {
+ launcher.launcherOpen = !launcher.launcherOpen;
+ }
+ }
+
+ LazyLoader {
+ id: loader
+ activeAsync: launcher.launcherOpen
+
+ PanelWindow {
+ width: 450
+ height: 7 + searchContainer.implicitHeight + list.topMargin * 2 + list.delegateHeight * 10
+ color: "transparent"
+
+
+ anchors {
+ bottom: parent.bottom
+ }
+
+
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+ WlrLayershell.namespace: "shell:launcher"
+
+ Rectangle {
+
+ height: 7 + searchContainer.implicitHeight + list.topMargin + list.bottomMargin + Math.min(list.contentHeight, list.delegateHeight * 10)
+ Behavior on height {
+ NumberAnimation {
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ }
+ width: 450
+ color: "#30c0afaf"
+ radius: 5
+ border.color: "black"
+ border.width: 2
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 7
+ anchors.bottomMargin: 0
+ spacing: 0
+
+ Rectangle {
+ id: searchContainer
+ Layout.fillWidth: true
+ implicitHeight: searchbox.implicitHeight + 10
+ color: "#30c0ffff"
+ radius: 3
+ border.color: "#50ffffff"
+
+ RowLayout {
+ id: searchbox
+ anchors.fill: parent
+ anchors.margins: 5
+
+ IconImage {
+ implicitSize: parent.height
+ source: "root:icons/magnifying-glass.svg"
+ }
+
+ TextInput {
+ id: search
+ Layout.fillWidth: true
+ color: "black"
+
+ focus: true
+ Keys.forwardTo: [list]
+ Keys.onEscapePressed: launcher.launcherOpen = false
+
+ Keys.onPressed: event => {
+ if (event.modifiers & Qt.ControlModifier) {
+ if (event.key == Qt.Key_J) {
+ list.currentIndex = list.currentIndex == list.count - 1 ? 0 : list.currentIndex + 1;
+ event.accepted = true;
+ } else if (event.key == Qt.Key_K) {
+ list.currentIndex = list.currentIndex == 0 ? list.count - 1 : list.currentIndex - 1;
+ event.accepted = true;
+ }
+ }
+ }
+
+ onAccepted: {
+ if (list.currentItem) {
+ list.currentItem.clicked(null);
+ }
+ }
+
+ onTextChanged: {
+ list.currentIndex = 0;
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: list
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ clip: true
+ cacheBuffer: 0 // works around QTBUG-131106
+ //reuseItems: true
+ model: ScriptModel {
+ values: DesktopEntries.applications.values.map(object => {
+ const stxt = search.text.toLowerCase();
+ const ntxt = object.name.toLowerCase();
+ let si = 0;
+ let ni = 0;
+
+ let matches = [];
+ let startMatch = -1;
+
+ for (let si = 0; si != stxt.length; ++si) {
+ const sc = stxt[si];
+
+ while (true) {
+ // Drop any entries with letters that don't exist in order
+ if (ni == ntxt.length)
+ return null;
+
+ const nc = ntxt[ni++];
+
+ if (nc == sc) {
+ if (startMatch == -1)
+ startMatch = ni;
+ break;
+ } else {
+ if (startMatch != -1) {
+ matches.push({
+ index: startMatch,
+ length: ni - startMatch
+ });
+
+ startMatch = -1;
+ }
+ }
+ }
+ }
+
+ if (startMatch != -1) {
+ matches.push({
+ index: startMatch,
+ length: ni - startMatch + 1
+ });
+ }
+
+ return {
+ object: object,
+ matches: matches
+ };
+ }).filter(entry => entry !== null).sort((a, b) => {
+ let ai = 0;
+ let bi = 0;
+ let s = 0;
+
+ while (ai != a.matches.length && bi != b.matches.length) {
+ const am = a.matches[ai];
+ const bm = b.matches[bi];
+
+ s = bm.length - am.length;
+ if (s != 0)
+ return s;
+
+ s = am.index - bm.index;
+ if (s != 0)
+ return s;
+
+ ++ai;
+ ++bi;
+ }
+
+ s = a.matches.length - b.matches.length;
+ if (s != 0)
+ return s;
+
+ s = a.object.name.length - b.object.name.length;
+ if (s != 0)
+ return s;
+
+ return a.object.name.localeCompare(b.object.name);
+ }).map(entry => entry.object)
+
+ onValuesChanged: list.currentIndex = 0
+ }
+
+ topMargin: 7
+ bottomMargin: list.count == 0 ? 0 : 7
+
+ add: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: 100
+ }
+ }
+
+ displaced: Transition {
+ NumberAnimation {
+ property: "y"
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ property: "opacity"
+ to: 1
+ duration: 100
+ }
+ }
+
+ move: Transition {
+ NumberAnimation {
+ property: "y"
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ property: "opacity"
+ to: 1
+ duration: 100
+ }
+ }
+
+ remove: Transition {
+ NumberAnimation {
+ property: "y"
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ NumberAnimation {
+ property: "opacity"
+ to: 0
+ duration: 100
+ }
+ }
+
+ highlight: Rectangle {
+ radius: 5
+ color: "#20e0ffff"
+ border.color: "#30ffffff"
+ border.width: 1
+ }
+ keyNavigationEnabled: true
+ keyNavigationWraps: true
+ highlightMoveVelocity: -1
+ highlightMoveDuration: 50
+ preferredHighlightBegin: list.topMargin
+ preferredHighlightEnd: list.height - list.bottomMargin
+ highlightRangeMode: ListView.ApplyRange
+ snapMode: ListView.SnapToItem
+
+ readonly property real delegateHeight: 44
+
+ delegate: MouseArea {
+ required property DesktopEntry modelData
+
+ implicitHeight: list.delegateHeight
+ implicitWidth: ListView.view.width
+
+ onClicked: {
+ modelData.execute();
+ launcher.launcherOpen = false;
+ }
+
+ RowLayout {
+ id: delegateLayout
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: 5
+ }
+
+ IconImage {
+ Layout.alignment: Qt.AlignVCenter
+ asynchronous: true
+ implicitSize: 30
+ source: Quickshell.iconPath(modelData.icon)
+ }
+ Text {
+ text: modelData.name
+ color: "black"
+ font.family: "ComicShannsMono Nerd Font Mono"
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ function init() {
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/notifications/Notification.qml b/modules/style/quickshell/shell/modules/notifications/Notification.qml
new file mode 100644
index 0000000..7c147e6
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/notifications/Notification.qml
@@ -0,0 +1,60 @@
+import QtQuick
+import QtQuick.Shapes
+
+import qs.config
+
+Shape {
+ id: root
+ anchors.top: parent.top
+ // anchors.right: parent.right
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ readonly property real rounding: Config.border.rounding
+
+ implicitWidth: 1000
+ implicitHeight: 300
+ ShapePath {
+ fillColor: Config.catppuccin.base
+ strokeWidth: -1
+
+ PathArc {
+ relativeX: root.rounding
+ relativeY: root.rounding
+ radiusX: root.rounding
+ radiusY: root.rounding
+ }
+ PathLine {
+ relativeX: 0
+ relativeY: 100
+ }
+ PathLine {
+ relativeX: 300
+ relativeY: 0
+ }
+ PathLine {
+ relativeX: 0
+ relativeY: -100
+ }
+ PathArc {
+ relativeX: root.rounding
+ relativeY: -root.rounding
+ radiusX: root.rounding
+ radiusY: root.rounding
+ }
+
+ Behavior on fillColor {
+ ColorAnimation {
+ duration: 500
+ easing.type: Easing.BezierSpline
+ }
+ }
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: "This is a test"
+ color: "white"
+ font.pointSize: 15
+ font.bold: true
+ }
+}
diff --git a/modules/style/quickshell/shell/modules/volume/VolumeSlider.qml b/modules/style/quickshell/shell/modules/volume/VolumeSlider.qml
new file mode 100644
index 0000000..e6004b4
--- /dev/null
+++ b/modules/style/quickshell/shell/modules/volume/VolumeSlider.qml
@@ -0,0 +1,82 @@
+pragma ComponentBehavior: Bound
+
+import Quickshell
+import QtQuick
+import QtQuick.Shapes
+import qs.config
+
+Rectangle {
+ id: root
+ required property ShellScreen screen
+ readonly property real rounding: Config.border.rounding
+ color: "transparent"
+
+ anchors {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
+
+ implicitWidth: Config.volumeslider.width
+ implicitHeight: screen.height / 3
+
+ Rectangle {
+ anchors.right: parent.right
+ color: "green"
+ implicitWidth: hover.hovered ? Config.volumeslider.width : Config.border.thickness
+
+ implicitHeight: root.screen.height / 3
+
+ HoverHandler {
+ id: hover
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ }
+
+ Behavior on implicitWidth {
+ NumberAnimation {
+ duration: 400
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+
+ Shape {
+ implicitWidth: hover.hovered ? Config.volumeslider.width : Config.border.thickness
+ implicitHeight: root.screen.height / 3
+ ShapePath {
+ fillColor: Config.catppuccin.base
+ strokeWidth: -1
+ PathArc {
+ relativeX: -root.rounding
+ relativeY: root.rounding
+ radiusX: root.rounding
+ radiusY: root.rounding
+ direction: PathArc.Counterclockwise
+ }
+ PathLine {
+ relativeY: root.screen.height / 3
+ relativeX: 0
+ }
+ PathArc {
+ relativeX: root.rounding
+ // relativeX: 50
+ relativeY: -root.rounding
+ radiusX: root.rounding
+ radiusY: root.rounding
+ // direction: PathArc.Counterclockwise
+ // useLargeArc: true
+ }
+ PathLine {
+ relativeX: 50
+ relativeY: 0
+ }
+ PathLine {
+ relativeY: -root.screen.height / 3
+ relativeX: 0
+ }
+ PathLine {
+ relativeX: -50
+ relativeY: 0
+ }
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/services/Hover.qml b/modules/style/quickshell/shell/services/Hover.qml
new file mode 100644
index 0000000..9e76fd3
--- /dev/null
+++ b/modules/style/quickshell/shell/services/Hover.qml
@@ -0,0 +1,49 @@
+import Quickshell
+import QtQuick
+import qs.config
+
+MouseArea {
+ id: root
+
+ anchors.fill: parent
+ hoverEnabled: true
+
+ required property ShellScreen screen
+ // required property Panels panels
+ required property Item bar
+
+ property bool showVolumeMenu: false
+ property bool isInRightPanel: false
+
+ // function withinPanelHeight(panel: Item, x: real, y: real): bool {
+ // const panelY = Config.border.thickness + panel.y;
+ // return y >= panelY - Config.border.rounding && y <= panelY + panel.height + Config.border.rounding;
+ // }
+
+ // function inLeftBorder(x: real, y: real): bool {
+ // return x <= Config.border.thickness;
+ // }
+
+ function inRightPanel(x: real, y: real): bool {
+ // Cursor is in middle veritcal third of screen
+ // Cursor is in the right border
+ return y >= root.screen.height / 3 && y <= (root.screen.height / 3) * 2 && x >= root.screen.width - Config.border.thickness;
+ }
+
+ // Update on mouse cursor movement
+ onPositionChanged: event => {
+ const x = event.x;
+ const y = event.y;
+
+ root.isInRightPanel = inRightPanel(x, y);
+
+ console.log("In right panel: " + root.isInRightPanel);
+
+ console.log("x:" + x + " y: " + y);
+ }
+ onContainsMouseChanged: {
+ if (!containsMouse) {
+ root.isInRightPanel = false;
+ }
+ }
+}
diff --git a/modules/style/quickshell/shell/services/Notification.qml b/modules/style/quickshell/shell/services/Notification.qml
new file mode 100644
index 0000000..bdf52de
--- /dev/null
+++ b/modules/style/quickshell/shell/services/Notification.qml
@@ -0,0 +1,20 @@
+pragma Singleton
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import Quickshell.Services.Notifications
+
+/**
+ * Provides extra features not in Quickshell.Services.Notifications:
+ * - Persistent storage
+ * - Popup notifications, with timeout
+ * - Notification groups by app
+ */
+Singleton {
+ id: root
+ NotificationServer {
+
+ }
+}
diff --git a/modules/style/quickshell/shell/services/niri/Niri.qml b/modules/style/quickshell/shell/services/niri/Niri.qml
new file mode 100644
index 0000000..de16157
--- /dev/null
+++ b/modules/style/quickshell/shell/services/niri/Niri.qml
@@ -0,0 +1,94 @@
+// Kind thanks to https://github.com/MapoMagpie/nixos-flakes/blob/main/home/ui/quickshell/config/Data/Niri.qml
+// This file was taken from there and further modified.
+
+pragma Singleton
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+
+Singleton {
+ id: root
+ // property var data
+ property var workspaces: []
+ property var activeWorkspace: "VOID"
+ property var activeWorkspaceIndex: 0
+ property var windows: []
+ // property var activedWindowId: 0
+
+ Process {
+ id: proc
+ command: ["niri", "msg", "-j", "event-stream"]
+
+ running: true
+ stdout: SplitParser {
+ onRead: data => {
+ var event = JSON.parse(data);
+ let workspaces = [];
+ if (event.WorkspacesChanged) {
+ root.workspaces = event.WorkspacesChanged.workspaces;
+ root.workspaces = root.workspaces.sort((a, b) => a.id - b.id);
+ root.activeWorkspaceIndex = root.workspaces.findIndex(w => w.is_focused);
+ if (root.activeWorkspaceIndex < 0) {
+ root.activeWorkspaceIndex = 0;
+ }
+ root.activeWorkspace = root.workspaces[root.activeWorkspaceIndex].name;
+ }
+ if (event.WindowsChanged) {
+ root.windows = [...event.WindowsChanged.windows].sort((a, b) => a.id - b.id);
+ }
+ if (event.WindowOpenedOrChanged) {
+ const window = event.WindowOpenedOrChanged.window;
+ const index = root.windows.findIndex(w => w.id === window.id);
+ // console.log("window opened or changed: ", index, ", win id: ", window.id);
+ if (index >= 0) {
+ // console.log("replace window, old: ", root.windows[index].id, ", new: ", window.id);
+ root.windows[index] = window;
+ } else {
+ // console.log("push window, new: ", window.id);
+ root.windows.push(window);
+ }
+ root.windows = [...root.windows.sort((a, b) => a.id - b.id)];
+ }
+ if (event.WindowClosed) {
+ const index = root.windows.findIndex(w => w.id === event.WindowClosed.id);
+ // console.log("window closed: ", index, ", win id: ", event.WindowClosed.id);
+ if (index >= 0) {
+ root.windows.splice(index, 1);
+ }
+ root.windows = [...root.windows.sort((a, b) => a.id - b.id)];
+ }
+ if (event.WorkspaceActivated) {
+ root.activeWorkspaceIndex = root.workspaces.findIndex(w => w.id === event.WorkspaceActivated.id);
+ if (root.activeWorkspaceIndex < 0) {
+ root.activeWorkspaceIndex = 0;
+ }
+ root.activeWorkspace = root.workspaces[root.activeWorkspaceIndex].name;
+ }
+ }
+ }
+ }
+ // component Workspace: QtObject {
+ // required property int id
+ // property int idx
+ // property string name: "VOID"
+ // required property string output
+ // property bool is_active
+ // property bool is_focused
+ // property int active_window_id
+ // }
+}
+
+// {
+// "workspaces": [
+// {
+// "id": 5,
+// "idx": 4,
+// "name": "GAME",
+// "output": "DP-3",
+// "is_active": false,
+// "is_focused": false,
+// "active_window_id": null
+// },
+// ]
+// }
diff --git a/modules/style/quickshell/shell/shell.qml b/modules/style/quickshell/shell/shell.qml
index e69de29..0ff4469 100644
--- a/modules/style/quickshell/shell/shell.qml
+++ b/modules/style/quickshell/shell/shell.qml
@@ -0,0 +1,15 @@
+//@ pragma Env QS_NO_RELOAD_POPUP=1
+
+import Quickshell
+import QtQuick
+
+import qs.modules.drawers
+import qs
+
+ShellRoot {
+ id: shellroot
+
+ Component.onCompleted: [Launcher.init(), AudioPopup.init()]
+
+ Drawers {}
+}
diff --git a/modules/style/theming.nix b/modules/style/theming.nix
deleted file mode 100644
index d276442..0000000
--- a/modules/style/theming.nix
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}: let
- inherit (builtins) toString isBool;
- inherit (lib.generators) toINI;
- inherit (lib.modules) mkMerge mkIf;
- inherit (lib.options) mkEnableOption;
- inherit (lib.strings) escape;
- inherit (lib.trivial) boolToString;
-
- cfg = config.modules.theming;
-
- toGtk3Ini = toINI {
- mkKeyValue = key: value: let
- value' =
- if isBool value
- then boolToString value
- else toString value;
- in "${escape ["="] key}=${value'}";
- };
-
- gtkIni = {
- gtk-application-prefer-dark-theme = 1;
- gtk-font-name = "Lexend 11";
- gtk-icon-theme-name = "Papirus-Dark";
- gtk-xft-antialias = 1;
- gtk-xft-hinting = 1;
- gtk-xft-hintstyle = "hintslight";
- gtk-xft-rgba = "rgb";
- gtk-cursor-theme-name = "BreezeX-RosePine-Linux";
- gtk-theme-name = "Gruvbox-Dark";
- };
-in {
- options.modules.theming = {
- qt.enable = mkEnableOption "qt theming";
- };
- config = mkMerge [
- (mkIf cfg.gtk.enable {
- environment = {
- systemPackages = builtins.attrValues {
- inherit (pkgs) rose-pine-cursor;
- };
- etc = {
- "xdg/gtk-4.0/settings.ini".text = toGtk3Ini {
- Settings = gtkIni;
- };
- "xdg/gtk-3.0/settings.ini".text = toGtk3Ini {
- Settings = gtkIni;
- };
-
- "xdg/gtk-2.0/gtkrc".text = ''
- gtk-cursor-theme-name = BreezeX-RosePine-Linux
- gtk-cursor-theme-size = 32
- gtk-theme-name = Gruvbox-Dark
- gtk-icon-theme-name = Papirus-Dark
- gtk-font-name = Lexend 11
- '';
-
- "xdg/Xresources".text = ''
- Xcursor.size: 32
- Xcursor.theme: BreezeX-RosePine-Linux
- '';
- };
- };
- })
- ];
-}
diff --git a/modules/style/wholefoods.jpg b/modules/style/wholefoods.jpg
deleted file mode 100644
index 3d9c47f..0000000
Binary files a/modules/style/wholefoods.jpg and /dev/null differ
diff --git a/modules/style/wholefoods.png b/modules/style/wholefoods.png
new file mode 100644
index 0000000..865da3d
Binary files /dev/null and b/modules/style/wholefoods.png differ
diff --git a/modules/system/boot/boot.mod.nix b/modules/system/boot/boot.mod.nix
new file mode 100644
index 0000000..5c05cb8
--- /dev/null
+++ b/modules/system/boot/boot.mod.nix
@@ -0,0 +1,97 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.modules) mkForce mkDefault;
+ inherit (lib.options) mkOption mkEnableOption;
+ inherit (lib.types) int;
+
+ cfg = config.modules.system.boot;
+in
+{
+ options.modules.system.boot = {
+ grub.enable = mkEnableOption "Grub, a bloated boot loader";
+ systemd-boot.enable = mkEnableOption "Poetteringboot";
+ timeout = mkOption {
+ description = ''
+ Set the boot loader's timeout. This is 0 by default, but preferably longer on remote servers to make switching to previous generations easier.
+ '';
+ type = int;
+ # I love spamming space
+ default = 0;
+ };
+ };
+ config = {
+ assertions = [
+ {
+ assertion = cfg.systemd-boot.enable || cfg.grub.enable;
+ message = "No bootloader is enabled.";
+ }
+ {
+ assertion =
+ cfg.systemd-boot.enable -> !cfg.grub.enable && cfg.grub.enable -> !cfg.systemd-boot.enable;
+ message = "Please enable only ONE of systemd-boot or grub.";
+ }
+ ];
+ boot = {
+ tmp.useTmpfs = true;
+ consoleLogLevel = 0;
+
+ kernelParams = [
+ "quiet"
+ "splash"
+ "rd.systemd.show_status=false"
+ "rd.udev.log_level=3"
+ "udev.log_priority=3"
+ "boot.shell_on_fail"
+ ];
+
+ initrd = {
+ verbose = false;
+ systemd.enable = true;
+ };
+ loader = {
+ efi.canTouchEfiVariables = true;
+ timeout = mkDefault cfg.timeout;
+ systemd-boot = {
+ inherit (cfg.systemd-boot) enable;
+ # INFO: Leaving this enabled is a security vulneratibility,
+ # since we can just start /bin/sh from there and get root access.
+ # Since I have FDE, this isn't _as_ critical, but it would still be
+ # a bad idea to leave it enabled
+ editor = mkForce false;
+ consoleMode = "auto";
+ configurationLimit = 5;
+ };
+ grub = {
+ inherit (cfg.grub) enable;
+ efiSupport = true;
+ device = "nodev";
+ configurationLimit = 5;
+ };
+ };
+ plymouth = {
+ enable = true;
+ themePackages = [
+ (pkgs.adi1090x-plymouth-themes.override {
+ selected_themes = [
+ "hud_3"
+ ];
+ })
+ ];
+ theme = "hud_3";
+ };
+ };
+ powerManagement = {
+ powerDownCommands = ''
+ ${pkgs.plymouth} --show-splash
+ '';
+ resumeCommands = ''
+ ${pkgs.plymouth} --quit
+ '';
+ };
+ };
+}
diff --git a/modules/system/boot/lanzaboote/module.nix b/modules/system/boot/lanzaboote.mod.nix
similarity index 57%
rename from modules/system/boot/lanzaboote/module.nix
rename to modules/system/boot/lanzaboote.mod.nix
index e7fd7a5..77fbf5f 100644
--- a/modules/system/boot/lanzaboote/module.nix
+++ b/modules/system/boot/lanzaboote.mod.nix
@@ -1,17 +1,25 @@
{
config,
- inputs,
lib,
pkgs,
+ sources,
...
}: let
inherit (lib.modules) mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ cfg = config.modules.system.boot.lanzaboote;
in {
+ options.modules.system.boot.lanzaboote.enable = mkEnableOption "Lanzaboote";
imports = [
- inputs.lanzaboote.nixosModules.lanzaboote
+ (import sources.flake-compat {
+ src = sources.lanzaboote;
+ copySourceTreeToStore = false;
+ useBuiltinsFetchTree = true;
+ }).outputs.nixosModules.lanzaboote
];
- config = mkIf false {
+ config = mkIf cfg.enable {
boot = {
lanzaboote = {
enable = true;
diff --git a/modules/system/boot/module.nix b/modules/system/boot/module.nix
deleted file mode 100644
index 275fa5d..0000000
--- a/modules/system/boot/module.nix
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}: let
- inherit (lib.modules) mkForce;
- inherit (lib.options) mkEnableOption;
-
- cfg = config.modules.system.boot;
-in {
- options.modules.system.boot = {
- grub.enable = mkEnableOption "Grub, a bloated boot loader";
- systemd-boot.enable = mkEnableOption "Poetteringboot";
- };
- config = {
- assertions = [
- {
- assertion = cfg.systemd-boot.enable || cfg.grub.enable;
- message = "No bootloader is enabled.";
- }
- {
- assertion = cfg.systemd-boot.enable -> !cfg.grub.enable && cfg.grub.enable -> !cfg.systemd-boot.enable;
- message = "Please enable only ONE of systemd-boot or grub.";
- }
- ];
- boot = {
- tmp.useTmpfs = true;
- initrd = {
- verbose = true;
- systemd.enable = true;
- };
- loader = {
- efi.canTouchEfiVariables = true;
- # I love spamming space
- timeout = 0;
- systemd-boot = {
- enable = cfg.systemd-boot.enable;
- editor = mkForce false;
- configurationLimit = 5;
- };
- grub = {
- enable = cfg.grub.enable;
- efiSupport = true;
- device = "nodev";
- configurationLimit = 5;
- };
- };
- plymouth = {
- enable = false;
- # font = "${pkgs.jetbrains-mono}/share/fonts/truetype/JetBrainsMono-Regular.ttf";
- themePackages = [pkgs.plymouth-matrix-theme];
- theme = "matrix";
- };
- };
- };
-}
diff --git a/modules/system/hardware/bluetooth.nix b/modules/system/hardware/bluetooth.mod.nix
similarity index 78%
rename from modules/system/hardware/bluetooth.nix
rename to modules/system/hardware/bluetooth.mod.nix
index b882011..ff68b93 100644
--- a/modules/system/hardware/bluetooth.nix
+++ b/modules/system/hardware/bluetooth.mod.nix
@@ -3,19 +3,20 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.modules) mkIf;
cfg = config.modules.system.hardware.bluetooth;
-in {
+in
+{
config = mkIf cfg.enable {
hardware.bluetooth = {
enable = true;
- powerOnBoot = mkIf cfg.powerOnBoot true;
+ inherit (cfg) powerOnBoot;
};
environment.systemPackages = builtins.attrValues {
- inherit
- (pkgs)
+ inherit (pkgs)
bluetuith
bluez
blueman
diff --git a/modules/system/hardware/cpu/amd.nix b/modules/system/hardware/cpu/amd.nix
deleted file mode 100644
index e69de29..0000000
diff --git a/modules/system/hardware/cpu/module.nix b/modules/system/hardware/cpu/module.nix
deleted file mode 100644
index 9b5284e..0000000
--- a/modules/system/hardware/cpu/module.nix
+++ /dev/null
@@ -1,3 +0,0 @@
-_: {
- imports = [./intel.nix];
-}
diff --git a/modules/system/hardware/graphics.mod.nix b/modules/system/hardware/graphics.mod.nix
new file mode 100644
index 0000000..063e26e
--- /dev/null
+++ b/modules/system/hardware/graphics.mod.nix
@@ -0,0 +1,70 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.modules) mkDefault mkIf;
+ inherit (lib.options) mkEnableOption;
+
+ cfg = config.modules.system.hardware;
+in
+{
+ options.modules.system.hardware = {
+ nvidia = {
+ enable = mkEnableOption "Nvidia graphics drivers";
+ };
+ amd.enable = mkEnableOption "AMD graphics drivers";
+ };
+ config = {
+ hardware = {
+ graphics = {
+ enable = true;
+ extraPackages = [ pkgs.nvidia-vaapi-driver ];
+ };
+
+ nvidia = mkIf cfg.nvidia.enable {
+ open = mkDefault true;
+
+ # Whether to enable kernel modesetting when using the NVIDIA proprietary driver.
+ # Enabling this causes the proprietary NVIDIA driver to provide its own
+ # framebuffer device, which can cause Wayland compositors to work when
+ # they otherwise wouldn’t. .
+ modesetting.enable = true;
+
+ # nvidia-settings is useless on NixOS.
+ nvidiaSettings = false;
+
+ # fixes sleep on nvidia devices
+ powerManagement = {
+ enable = true;
+ finegrained = false;
+ };
+ package = config.boot.kernelPackages.nvidiaPackages.beta;
+ };
+ };
+ services.xserver.videoDrivers = [
+ "nvidia"
+ ];
+
+ environment.systemPackages = builtins.attrValues {
+ inherit (pkgs)
+ mesa
+ vulkan-tools
+ vulkan-loader
+ libva
+ libva-utils
+ ;
+ inherit (pkgs.nvtopPackages) nvidia;
+ };
+ # Nouveau is a set of free and open-source drivers for NVIDIA GPUs
+ # that provide 2D/3D acceleration for all NVIDIA GPUs.
+ # Its use is in general not recommended due to its considerably worse
+ # performance compared to NVIDIA's kernel modules, as it does not
+ # support reclocking (changing the GPU clock frequency on-demand)
+ # for many NVIDIA GPUs.
+ # I therefore disable it to save myself from headaches.
+ boot.blacklistedKernelModules = mkIf cfg.nvidia.enable [ "nouveau" ];
+ };
+}
diff --git a/modules/system/hardware/graphics.nix b/modules/system/hardware/graphics.nix
deleted file mode 100644
index 25e255e..0000000
--- a/modules/system/hardware/graphics.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- config,
- lib,
- ...
-}: let
- inherit (lib.modules) mkIf;
- inherit (lib.options) mkEnableOption;
-
- cfg = config.modules.system.hardware;
-in {
- options.modules.system.hardware = {
- nvidia = {
- enable = mkEnableOption "Nvidia graphics drivers";
- };
- amd.enable = mkEnableOption "AMD graphics drivers";
- };
- config = {
- hardware = {
- graphics.enable = true;
- nvidia = mkIf cfg.nvidia.enable {
- # we want the open-source drivers
- open = true;
-
- modesetting.enable = true;
- nvidiaSettings = false;
-
- # fixes sleep on nvidia devices
- powerManagement = {
- enable = true;
- finegrained = false;
- };
- package = config.boot.kernelPackages.nvidiaPackages.beta;
- };
- };
- services.xserver.videoDrivers = mkIf cfg.nvidia.enable ["nvidia"];
- };
-}
diff --git a/modules/system/hardware/cpu/intel.nix b/modules/system/hardware/intel.mod.nix
similarity index 92%
rename from modules/system/hardware/cpu/intel.nix
rename to modules/system/hardware/intel.mod.nix
index ba82da7..863d9a3 100644
--- a/modules/system/hardware/cpu/intel.nix
+++ b/modules/system/hardware/intel.mod.nix
@@ -3,19 +3,20 @@
lib,
pkgs,
...
-}: let
+}:
+let
inherit (lib.modules) mkDefault mkIf;
inherit (lib.options) mkEnableOption;
cfg = config.modules.system.hardware.intel;
-in {
+in
+{
options.modules.system.hardware.intel.enable = mkEnableOption "Intel Hardware";
config = mkIf cfg.enable {
hardware = {
cpu.intel.updateMicrocode = mkDefault config.hardware.enableRedistributableFirmware;
graphics.extraPackages = builtins.attrValues {
- inherit
- (pkgs)
+ inherit (pkgs)
intel-vaapi-driver
intel-media-driver
;
diff --git a/modules/system/hardware/irqbalance.mod.nix b/modules/system/hardware/irqbalance.mod.nix
new file mode 100644
index 0000000..56f3198
--- /dev/null
+++ b/modules/system/hardware/irqbalance.mod.nix
@@ -0,0 +1,3 @@
+_: {
+ services.irqbalance.enable = true;
+}
diff --git a/modules/system/hardware/keyboard/default.nix b/modules/system/hardware/keyboard.mod.nix
similarity index 100%
rename from modules/system/hardware/keyboard/default.nix
rename to modules/system/hardware/keyboard.mod.nix
diff --git a/modules/system/hardware/module.nix b/modules/system/hardware/module.nix
deleted file mode 100644
index 6efca34..0000000
--- a/modules/system/hardware/module.nix
+++ /dev/null
@@ -1,5 +0,0 @@
-_: {
- imports = [./bluetooth.nix ./keyboard ./graphics.nix];
-
- services.irqbalance.enable = true;
-}
diff --git a/modules/system/hardware/sound/pipewire/module.nix b/modules/system/hardware/pipewire.mod.nix
similarity index 96%
rename from modules/system/hardware/sound/pipewire/module.nix
rename to modules/system/hardware/pipewire.mod.nix
index a18d3de..9a61f76 100644
--- a/modules/system/hardware/sound/pipewire/module.nix
+++ b/modules/system/hardware/pipewire.mod.nix
@@ -2,11 +2,13 @@
config,
lib,
...
-}: let
+}:
+let
inherit (lib.modules) mkIf;
cfg = config.modules.system.sound;
-in {
+in
+{
config = mkIf cfg.enable {
services.pipewire = {
enable = true;
diff --git a/modules/system/hardware/power.mod.nix b/modules/system/hardware/power.mod.nix
new file mode 100644
index 0000000..9d9c53d
--- /dev/null
+++ b/modules/system/hardware/power.mod.nix
@@ -0,0 +1,243 @@
+{
+ config,
+ sources,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.modules) mkDefault;
+in
+{
+ imports = [
+ # (sources.watt + "/nix/module.nix")
+ ];
+ config = {
+ environment.systemPackages = builtins.attrValues {
+ inherit (pkgs)
+ acpi
+ powertop
+ ;
+ };
+
+ boot = {
+ kernelModules = [ "acpi_call" ];
+ extraModulePackages = with config.boot.kernelPackages; [
+ acpi_call
+ cpupower
+ ];
+ };
+
+ hardware.acpilight.enable = false;
+
+ # services.watt = {
+ # enable = true;
+ # # sample config from https://github.com/NotAShelf/watt#sample-configuration
+ # settings = {
+ # rule = [
+ # {
+ # cpu = {
+ # energy-performance-preference = "power";
+ # frequency-mhz-maximum = 2000;
+ # governor = "powersave";
+ # turbo = false;
+ # };
+ # "if" = {
+ # is-more-than = 85;
+ # value = "$cpu-temperature";
+ # };
+ # priority = 100;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "power";
+ # frequency-mhz-maximum = 800;
+ # governor = "powersave";
+ # turbo = false;
+ # };
+ # "if" = {
+ # all = [
+ # "?discharging"
+ # {
+ # is-less-than = 0.3;
+ # value = "%power-supply-charge";
+ # }
+ # ];
+ # };
+ # power = {platform-profile = "low-power";};
+ # priority = 90;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "performance";
+ # governor = "performance";
+ # turbo = true;
+ # };
+ # "if" = {
+ # all = [
+ # {
+ # is-more-than = 0.8;
+ # value = "%cpu-usage";
+ # }
+ # {
+ # is-less-than = 30;
+ # value = "$cpu-idle-seconds";
+ # }
+ # {
+ # is-less-than = 75;
+ # value = "$cpu-temperature";
+ # }
+ # ];
+ # };
+ # priority = 80;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-bias = "balance_performance";
+ # energy-performance-preference = "performance";
+ # governor = "performance";
+ # turbo = true;
+ # };
+ # "if" = {
+ # all = [
+ # {not = "?discharging";}
+ # {
+ # is-more-than = 0.1;
+ # value = "%cpu-usage";
+ # }
+ # {
+ # is-less-than = 80;
+ # value = "$cpu-temperature";
+ # }
+ # ];
+ # };
+ # priority = 70;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "balance_performance";
+ # governor = "schedutil";
+ # };
+ # "if" = {
+ # all = [
+ # {
+ # is-more-than = 0.4;
+ # value = "%cpu-usage";
+ # }
+ # {
+ # is-less-than = 0.8;
+ # value = "%cpu-usage";
+ # }
+ # ];
+ # };
+ # priority = 60;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "power";
+ # governor = "powersave";
+ # turbo = false;
+ # };
+ # "if" = {
+ # all = [
+ # {
+ # is-less-than = 0.2;
+ # value = "%cpu-usage";
+ # }
+ # {
+ # is-more-than = 60;
+ # value = "$cpu-idle-seconds";
+ # }
+ # ];
+ # };
+ # priority = 50;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "power";
+ # frequency-mhz-maximum = 1600;
+ # governor = "powersave";
+ # turbo = false;
+ # };
+ # "if" = {
+ # is-more-than = 300;
+ # value = "$cpu-idle-seconds";
+ # };
+ # priority = 40;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "power";
+ # frequency-mhz-maximum = 2000;
+ # governor = "powersave";
+ # turbo = false;
+ # };
+ # "if" = {
+ # all = [
+ # "?discharging"
+ # {
+ # is-less-than = 0.5;
+ # value = "%power-supply-charge";
+ # }
+ # ];
+ # };
+ # power = {platform-profile = "low-power";};
+ # priority = 30;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-bias = "balance_power";
+ # energy-performance-preference = "power";
+ # frequency-mhz-maximum = 1800;
+ # frequency-mhz-minimum = 200;
+ # governor = "powersave";
+ # turbo = false;
+ # };
+ # "if" = "?discharging";
+ # priority = 20;
+ # }
+ # {
+ # cpu = {
+ # energy-performance-preference = "balance_performance";
+ # governor = "schedutil";
+ # };
+ # priority = 0;
+ # }
+ # ];
+ # };
+ # };
+ services = {
+ upower = {
+ enable = true;
+ percentageLow = 15;
+ percentageCritical = 5;
+ };
+
+ acpid = {
+ enable = true;
+ logEvents = true;
+ };
+
+ auto-cpufreq = {
+ enable = false;
+ settings = {
+ charger = {
+ governor = "performance";
+ energy_performance_preference = "performance";
+ scaling_min_freq = mkDefault 1800000;
+ scaling_max_freq = mkDefault 3800000;
+ turbo = "auto";
+ };
+
+ battery = {
+ governor = "powersave";
+ energy_performance_preference = "power";
+ scaling_min_freq = mkDefault 1200000;
+ scaling_max_freq = mkDefault 1800000;
+ turbo = "never";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/system/hardware/ram/module.nix b/modules/system/hardware/ram.mod.nix
similarity index 100%
rename from modules/system/hardware/ram/module.nix
rename to modules/system/hardware/ram.mod.nix
diff --git a/modules/system/hardware/wifi.nix b/modules/system/hardware/wifi.nix
deleted file mode 100644
index e69de29..0000000
diff --git a/modules/system/module.nix b/modules/system/module.nix
deleted file mode 100644
index 8fc29c9..0000000
--- a/modules/system/module.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-{config, ...}: let
- machine-id = builtins.substring 0 23 (builtins.hashString "sha256" config.networking.hostName);
-in {
- system = {
- # faster rebuilds
- switch = {
- enable = false;
- enableNg = true;
- };
- # our state version
- stateVersion = "23.11";
- };
- environment.etc."machine-id".text = "${machine-id}\n";
-}
diff --git a/modules/system/nix/documentation.nix b/modules/system/nix/documentation.mod.nix
similarity index 78%
rename from modules/system/nix/documentation.nix
rename to modules/system/nix/documentation.mod.nix
index b3afd60..41b5337 100644
--- a/modules/system/nix/documentation.nix
+++ b/modules/system/nix/documentation.mod.nix
@@ -3,13 +3,16 @@
documentation = {
# whether to enable the `doc` output of packages
# generally in ${pkg}/share/ as plaintext or html
- # # can shave off a few megabytes
+ # can shave off a few megabytes
doc.enable = false;
# whether to install the `info` command and the `info`
# output of packages
info.enable = false;
+ # Whether to install documentation targeted at developers.
+ dev.enable = true;
+
man = {
# Whether to install manual pages
# this means packages that provide a `man` output will have said output
@@ -22,7 +25,7 @@
generateCaches = false;
# Whether to enable mandoc as the default man page viewer.
- mandoc.enable = false; # my default manpage viewer is Neovim, so this isn't necessary
+ mandoc.enable = false; # my default manpage viewer is bat, so this isn't necessary
};
};
}
diff --git a/modules/system/nix/module.nix b/modules/system/nix/nix.mod.nix
similarity index 66%
rename from modules/system/nix/module.nix
rename to modules/system/nix/nix.mod.nix
index 1472607..5eb3fe2 100644
--- a/modules/system/nix/module.nix
+++ b/modules/system/nix/nix.mod.nix
@@ -1,57 +1,66 @@
# credits to raf
{
- config,
- inputs,
+ sources,
lib,
pkgs,
...
-}: let
- inherit (lib.attrsets) mapAttrsToList;
+}:
+let
inherit (lib.modules) mkForce;
-in {
- imports = [
- ./documentation.nix # nixos documentation
- ./nixpkgs.nix # global nixpkgs configuration
- ];
+ lix = pkgs.callPackage "${sources.lix}/package.nix" {
+ stdenv = pkgs.clangStdenv;
+ };
+in
+{
nix = {
- package = pkgs.lix;
+ package = lix;
+ # Check that Nix can parse the generated nix.conf.
+ checkConfig = true;
+
+ # Check the nix.conf, parsing for any kind of error. When disabled, checks only for unknown settings.
+ checkAllErrors = true;
# fuck channels, no thanks
channel.enable = mkForce false;
# this is taken from sioodmy.
# pin the registry to avoid downloading and evaling a new nixpkgs version every time
- registry =
- lib.mapAttrs (_: v: {flake = v;}) inputs
- // {system.flake = inputs.self;};
+ # registry =
+ # lib.mapAttrs (_: v: {flake = v.outPath;}) sources
+ # // {system.flake = sources.nichts;};
+ registry.nixpkgs.to = {
+ type = "path";
+ source = sources.nixpkgs;
+ };
- # This will additionally add your inputs to the system's legacy channels
- # Making legacy nix commands consistent as well
- nixPath = mapAttrsToList (key: _: "${key}=flake:${key}") config.nix.registry;
+ # Add inputs to the system's legacy channels
+ # to make legacy nix commands consistent as well
+ nixPath = [ "nixpkgs=/etc/nixos/nixpkgs" ];
- # Run the Nix daemon on lowest possible priority so that my system
- # stays responsive during demanding tasks such as GC and builds.
- # This is especially useful while auto-gc and auto-upgrade are enabled
- # as they can be quite demanding on the CPU.
+ # Run the Nix daemon on lowest possible priority
daemonCPUSchedPolicy = "idle";
daemonIOSchedClass = "idle";
daemonIOSchedPriority = 7;
# Collect garbage
+ # NOTE: I use nh for this.
gc = {
automatic = false;
dates = "20:00";
options = "--delete-older-than 7d";
- persistent = false; # don't try to catch up on missed GC runs
+ persistent = false;
};
# Automatically optimize nix store by removing hard links
optimise = {
automatic = true;
- dates = ["21:00"];
+ dates = [ "21:00" ];
};
+ # NOTE:
+ # Writes the settings to /etc/nix/nix.conf.
+ # See `man nix.conf` for more detailed descriptions of these settings.
settings = {
# Tell nix to use the xdg spec for base directories
# while transitioning, any state must be carried over
@@ -61,16 +70,31 @@ in {
# Automatically optimise symlinks
auto-optimise-store = true;
- # Allow sudo users to mark the following values as trusted
- allowed-users = ["root" "@wheel" "nix-builder"];
+ # Users that are allowed to connect to the Nix daemon.
+ allowed-users = [
+ "root"
+ "@wheel"
+ "nix-builder"
+ ];
- # Only allow sudo users to manage the nix store
- trusted-users = ["root" "@wheel" "nix-builder"];
+ # Users that are allowed to connect to the Nix daemon.
+ trusted-users = [
+ "root"
+ "@wheel"
+ "nix-builder"
+ ];
# Let the system decide the number of max jobs
# based on available system specs. Usually this is
# the same as the number of cores your CPU has.
- max-jobs = 2;
+ max-jobs = "auto";
+
+ # This option defines the maximum number of concurrent tasks during one build.
+ # It affects, e.g., -j option for make. The special value 0 means that the builder
+ # should use all available CPU cores in the system. Some builds may become
+ # non-deterministic with this option; use with care!
+ # Packages will only be affected if enableParallelBuilding is set for them.
+ cores = 0;
# If set, Nix will perform builds in a sandboxed environment
# that it will set up automatically for each build.
@@ -99,6 +123,7 @@ in {
"flakes" # flakes
"nix-command" # experimental nix commands
"cgroups" # allow nix to execute builds inside cgroups
+ "pipe-operator"
];
# Ensures that the result of Nix expressions is fully determined by
@@ -132,21 +157,27 @@ in {
keep-derivations = true;
keep-outputs = true;
- # Use binary cache, this is not Gentoo
- # external builders can also pick up those substituters
+ # Use binary cache
builders-use-substitutes = true;
# Substituters to pull from.
substituters = [
- "https://cache.nixos.org" # funny binary cache
+ "https://cache.nixos.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
];
+ # Determinate nix config
+ # ===========================================
+ # lazy-trees = true;
+ # ===========================================
};
};
+ environment.etc = {
+ "nixos/nixpkgs".source = builtins.storePath sources.nixpkgs;
+ };
systemd.services = {
# WE DONT WANT TO BUILD STUFF ON TMPFS
# ITS NOT A GOOD IDEA
diff --git a/modules/system/nix/nixpkgs.mod.nix b/modules/system/nix/nixpkgs.mod.nix
new file mode 100644
index 0000000..2955c48
--- /dev/null
+++ b/modules/system/nix/nixpkgs.mod.nix
@@ -0,0 +1,60 @@
+{
+ lib,
+ sources,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.options) mkOption;
+ inherit (lib.types) str;
+in
+{
+ imports = [
+ # Going full schizo
+ "${sources.nixpkgs}/nixos/modules/misc/nixpkgs/read-only.nix"
+ ];
+
+ options.nixpkgs.system = mkOption {
+ type = str;
+ default = pkgs.system;
+ readOnly = true;
+ };
+
+ config.nixpkgs.pkgs = import sources.nixpkgs {
+ inherit (pkgs.stdenv) hostPlatform;
+ overlays = [ ];
+ config = {
+ # Configuration reference:
+ #