feat: add window manager options

This commit is contained in:
danny 2026-02-04 18:21:40 +08:00
parent b4b7997ac5
commit 601dfb9217
31 changed files with 2006 additions and 821 deletions

View file

@ -12,9 +12,8 @@ in
systemConf = {
inherit hostname username;
enableHomeManager = true;
windowManager = "niri";
nvidia.enable = true;
hyprland.enable = false;
niri.enable = true;
sddm.package = (
pkgs.sddm-astronaut.override {
embeddedTheme = "purple_leaves";

View file

@ -1,7 +1,6 @@
{
imports = [
../../../modules/postgresql.nix
# ./mail.nix
./nginx.nix
# ./pangolin.nix
];

View file

@ -1,205 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkIf;
inherit (config.networking) domain;
mkCondition = (
condition: ithen: ielse: [
{
"if" = condition;
"then" = ithen;
}
{ "else" = ielse; }
]
);
rspamdWebPort = 11333;
rspamdPort = 31009;
fqdn = "mx1.dnywe.com";
rspamdSecretFile = config.sops.secrets."rspamd".path;
rspamdSecretPath = "/run/rspamd/rspamd-controller-password.inc";
in
{
networking.firewall.allowedTCPPorts = [ 8080 ];
imports = [
(import ../../../modules/stalwart.nix {
inherit domain;
enableNginx = false;
adminPassFile = config.sops.secrets."stalwart/adminPassword".path;
certs."default" = {
default = true;
cert = "%{file:${config.security.acme.certs.${fqdn}.directory}/cert.pem}%";
private-key = "%{file:${config.security.acme.certs.${fqdn}.directory}/key.pem}%";
};
ldapConf = {
type = "ldap";
url = "ldaps://ldap.net.dn";
tls.enable = true;
timeout = "30s";
base-dn = "ou=people,dc=net,dc=dn";
attributes = {
name = "uid";
email = "mail";
email-alias = "mailRoutingAddress";
secret = "userPassword";
description = [
"cn"
"description"
];
class = "objectClass";
groups = [ "memberOf" ];
};
filter = {
name = "(&(objectClass=inetOrgPerson)(|(uid=?)(mail=?)(mailRoutingAddress=?)))";
email = "(&(objectClass=inetOrgPerson)(|(mailRoutingAddress=?)(mail=?)))";
};
bind = {
dn = "cn=admin,dc=net,dc=dn";
secret = "%{file:${config.sops.secrets."stalwart/ldap".path}}%";
auth = {
method = "default";
};
};
};
})
];
services.stalwart-mail.settings.spam-filter.enable = !config.services.rspamd.enable;
services.stalwart-mail.settings.session.milter."rspamd" = mkIf config.services.rspamd.enable {
enable = mkCondition "listener = 'smtp'" true false;
hostname = "127.0.0.1";
port = rspamdPort;
stages = [
"connect"
"ehlo"
"mail"
"rcpt"
"data"
];
tls = false;
allow-invalid-certs = false;
options = {
tempfail-on-error = true;
max-response-size = 52428800; # 50mb
version = 6;
};
};
services.rspamd = {
enable = true;
locals = {
"redis.conf".text = ''
servers = "${config.services.redis.servers.rspamd.unixSocket}";
'';
"classifier-bayes.conf".text = ''
backend = "redis";
autolearn = true;
'';
"dkim_signing.conf".text = ''
enabled = false;
'';
"milter_headers.conf".text = ''
enabled = true;
extended_spam_headers = true;
skip_local = false;
use = ["x-spamd-bar", "x-spam-level", "x-spam-status", "authentication-results", "x-spamd-result"];
authenticated_headers = ["authentication-results"];
'';
};
localLuaRules =
pkgs.writeText "rspamd-local.lua"
# lua
''
-- Temporary fix for double dot issue rspamd#5273
local lua_util = require("lua_util")
rspamd_config.UNQUALIFY_SENDER_HOSTNAME = {
callback = function(task)
local hn = task:get_hostname()
if not hn then return end
local san_hn = string.gsub(hn, "%.$", "")
if hn ~= san_hn then
task:set_hostname(san_hn)
end
end,
type = "prefilter",
priority = lua_util.symbols_priorities.top + 1,
}
'';
workers = {
rspamd_proxy = {
type = "rspamd_proxy";
includes = [ "$CONFDIR/worker-proxy.inc" ];
bindSockets = [
"*:${toString rspamdPort}"
];
extraConfig = ''
self_scan = yes;
'';
};
controller = {
type = "controller";
includes = [
"$CONFDIR/worker-controller.inc"
];
extraConfig = ''
.include(try=true; priority=1,duplicate=merge) "${rspamdSecretPath}"
'';
bindSockets = [ "127.0.0.1:${toString rspamdWebPort}" ];
};
};
overrides."whitelist.conf".text = ''
whiltelist_from {
${domain} = true;
}
'';
};
systemd.services.rspamd = mkIf config.services.rspamd.enable {
path = [
pkgs.rspamd
pkgs.coreutils
];
serviceConfig = {
ExecStartPre = [
"${pkgs.writeShellScript "generate-rspamd-passwordfile" ''
RSPAMD_PASSWORD_HASH=$(rspamadm pw --password $(cat ${rspamdSecretFile}))
echo "enable_password = \"$RSPAMD_PASSWORD_HASH\";" > ${rspamdSecretPath}
chmod 770 "${rspamdSecretPath}"
''}"
];
};
};
services.redis.servers.rspamd = {
enable = true;
port = 0;
user = config.services.rspamd.user;
};
security.acme = {
acceptTerms = true;
certs."${fqdn}" = {
inheritDefaults = false;
group = config.systemd.services.stalwart-mail.serviceConfig.Group;
dnsProvider = "cloudflare";
dnsResolver = "1.1.1.1:53";
server = "https://acme-v02.api.letsencrypt.org/directory";
validMinDays = 30;
email = "dachxy@${domain}";
extraDomainNames = [ domain ];
environmentFile = config.sops.secrets."cloudflare/secret".path;
postRun = ''
systemctl reload stalwart-mail
'';
};
};
}

View file

@ -32,9 +32,9 @@ in
mode = "0660";
};
}
// (optionalAttrs config.services.stalwart-mail.enable (
// (optionalAttrs config.services.stalwart.enable (
let
inherit (config.users.users.stalwart-mail) name group;
inherit (config.users.users.stalwart) name group;
owner = name;
in
{

View file

@ -1,5 +1,5 @@
{
imports = [
# ../../../modules/davinci-resolve.nix
../../../modules/davinci-resolve.nix
];
}

View file

@ -1,8 +1,8 @@
{
imports = [
../../../modules/localsend.nix
./airplay.nix
./davinci-resolve.nix
# ./airplay.nix
# ./davinci-resolve.nix
# ./blender.nix
];
}

View file

@ -1,4 +1,3 @@
{ config, ... }:
{
imports = [
./actual-budget.nix
@ -15,5 +14,6 @@
./dns.nix
./acme.nix
./ntfy.nix
./homepage.nix
];
}

View file

@ -102,7 +102,7 @@ in
"192.168.100.0/24"
];
dns.port = 5300;
yaml-settings = {
settings = {
webservice.webserver = true;
recordcache.max_negative_ttl = 60;
};

View file

@ -0,0 +1,194 @@
{ config, ... }:
let
inherit (config.networking) domain;
cfg = config.services.homepage-dashboard;
in
{
sops.secrets = {
"homepage" = {
};
};
services.homepage-dashboard = {
enable = true;
openFirewall = true;
listenPort = 8044;
environmentFile = config.sops.secrets."homepage".path;
allowedHosts = "www.${domain},${domain},localhost:${toString cfg.listenPort}";
docker = {
docker = {
socket = "/var/run/docker.sock";
};
};
widgets = [
{
search = {
provider = "duckduckgo";
target = "_blank";
};
}
{
datetime = {
text_size = "x1";
format = {
dateStyle = "short";
timeStyle = "short";
hour12 = true;
};
};
}
];
services = [
{
"Files & Documents" = [
{
"Nextcloud" = {
icon = "nextcloud.svg";
description = " Cloud drive";
href = "https://${config.services.nextcloud.hostName}";
widgets = [
{
type = "nextcloud";
url = "https://${config.services.nextcloud.hostName}";
key = "{{HOMEPAGE_VAR_NEXTCLOUD_NC_TOKEN}}";
}
];
};
}
{
"Paperless" = {
icon = "paperless.svg";
description = "PDF editing, tagging, and viewing";
href = config.services.paperless.settings.PAPERLESS_URL;
};
}
];
}
{
"VPN & IDP" = [
{
"Netbird" = {
icon = "netbird.svg";
description = "VPN Service: access internal services";
href = "https://${config.services.netbird.server.domain}";
};
}
{
"Keycloak" = {
icon = "keycloak.svg";
description = "Identity provider";
href = "https://${config.services.keycloak.settings.hostname}";
};
}
];
}
{
"Monitor" = [
{
"Grafana" = {
icon = "grafana.svg";
description = "Show metrics!";
href = config.services.grafana.settings.server.root_url;
};
}
{
"Prometheus" = {
icon = "prometheus.svg";
description = "The web is not that useful 🥀";
href = config.services.prometheus.webExternalUrl;
};
}
{
"Uptime Kuma" = {
icon = "uptime-kuma.svg";
description = "Service health check";
href = "https://uptime.${domain}";
};
}
];
}
{
"Utility" = [
{
"Vaultwarden" = {
icon = "vaultwarden-light.svg";
description = "Password manager";
href = config.services.vaultwarden.config.DOMAIN;
};
}
{
"PowerDNS" = {
icon = "powerdns.svg";
description = "DNS record management";
href = "https://powerdns.${domain}";
};
}
{
"Actual Budget" = {
icon = "actual-budget.svg";
description = "Financial budget management";
href = "https://actual.${domain}";
};
}
{
"Ntfy" = {
icon = "ntfy.svg";
description = "Notification service";
href = config.services.ntfy-sh.settings.base-url;
};
}
];
}
{
"Games" = [
{
"Minecraft" = {
icon = "minecraft.svg";
description = "Minecraft servers";
widgets = [
{
type = "minecraft";
fields = [
"players"
"version"
"status"
];
url = "udp://mc.${domain}:${toString config.services.velocity.port}";
}
];
};
}
];
}
];
settings = {
base = "https://www.${domain}";
headerStyle = "boxed";
title = "DN Home";
description = "Welcome! maybe?";
disableUpdateCheck = true;
providers = {
};
quicklaunch = {
searchDescriptions = true;
hideInternetSearch = true;
showSearchSuggestions = true;
hideVisitURL = true;
provider = "google";
};
};
};
services.nginx.virtualHosts."${domain}" = {
useACMEHost = domain;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString cfg.listenPort}";
};
serverAliases = [
"www.${domain}"
];
};
}

View file

@ -65,6 +65,7 @@ forgejo:
password: ENC[AES256_GCM,data:dcIotYpgtdFLcunAB3ttlczzQ68=,iv:vH3rckAfntFAEtH3dolF7NCAdj142cAzre56x7oBdDA=,tag:TaxRn8g/TVloM60D6Ud0Jg==,type:str]
velocity: ENC[AES256_GCM,data:PYGSXfivm7OyKhBMKPOVDs+efpcb0hhwCAxlT05pM+kg9t0lH4TEMuxBXFRs80LUiQx+CYXyw8UvBkkKwPEc,iv:PppenjXIQ+eirCor3PxT16r2S7wO8bww5v/RyjQh9MI=,tag:Dc3BzmyQcTwYsvWShQ/JqQ==,type:str]
fabricProxy: ENC[AES256_GCM,data:srGYmqHgfkxAKKSjy9uGX1mQpE3N0rXb06MYiycbYESj/sZu/vjsPspvUdzTHHb9zkF5SWLWkmP6llIpimkss/dm7A1pGlagin3+,iv:yoWQdWeP9UjoRO5rJ9FQGbBu3iypIdXGrSDqBfFhw6w=,tag:+d/Tp/m3vENZAXJyHOMJEA==,type:str]
homepage: ENC[AES256_GCM,data:SqRtz4xrwCTQulgFsRAgTcQNQZRyRes+K4UzlhQZW3GuAmQaRBIHJyFttgIQYDKlc21QAiM2zxa9IBOtGNpQAdkplUptaRTq0fZH/OYcNw==,iv:NLEiWjfPvsw2Tq+pqrIPy8pKlWFD8wVXt9TMH8Y8+jA=,tag:N91DrYQoYeyJ3AH3ujTI+g==,type:str]
sops:
age:
- recipient: age17rjcght2y5p4ryr76ysnxpy2wff62sml7pyc5udcts48985j05vqpwdfq2
@ -94,7 +95,7 @@ sops:
OFloWEFuTC9GTXJsMG5NNktmdmIrY1kK0yN0ae0xNaydujV5lt2FiwXdyursG0DK
9i/B3TTAm9csDMMSTSFbiAUJDzG7kIqn++JU/cxvsGScSnhMqjEK/g==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-01-20T06:31:45Z"
mac: ENC[AES256_GCM,data:ad8EP8zk6mxlmMZaEijW0NWF72y2EikJPct7qxiCp6/sWGKKrGv8mRnC1zahgpRqpGR0jZKQ8Ot204EdGrJF9WI03+ZB9GgKi9ipQvXlGOCJq6m/Mp6WygI2hFAzRKCeoPqAPjVQxQ3Ctt/WEYXzvEp7CIKUq7WD6gTEFk6FDg0=,iv:20rJb79QnUW0DFbXTr0XXjiXjm7bK0CVs4oVan5SAKw=,tag:+mnMTBYQ1fhwe/abwGYNOA==,type:str]
lastmodified: "2026-01-30T04:08:29Z"
mac: ENC[AES256_GCM,data:egK9zlAccBV2IeJ+DYTP3AKQUUMFPmts8eZMilQlyh+EE/oXhNnKeKkmNg9h1RwoZ6zh0LRDsyjubCc06PI/wVx2lJ0JfPs4bt6PckC1hZglRSHHjmocyx1eF5bMVfBLmluDzQ3Zms1Ryvuh+M+EjtdhttBljAIb0JIRx8Wzwks=,iv:wWrRiOvzZDboZSMgTzmbVVWzpSIhLdlgxgUIFXCFet0=,tag:YLBtLivKLBvByyfm4PbVXQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

View file

@ -27,6 +27,16 @@ in
};
workspaces."game" = { };
window-rules = [
# Wine systray
{
matches = [
{
title = "^$";
app-id = "^steam_app_(.*)$";
}
];
open-fullscreen = false;
}
# Steam Game Fullscreen
{
matches = [
@ -68,5 +78,8 @@ in
}
];
};
wayland.windowManager.mango.settings = ''
xkb_rules_options = caps:escape
'';
};
}

View file

@ -11,8 +11,7 @@ in
inherit hostname username;
domain = "net.dn";
enableHomeManager = true;
hyprland.enable = false;
niri.enable = true;
windowManager = "niri";
face = pkgs.fetchurl {
url = "https://git.dnywe.com/dachxy/skydrive-avatar/raw/branch/main/skydrive.jpg";
hash = "sha256-aMjl6VL1Zy+r3ElfFyhFOlJKWn42JOnAFfBXF+GPB/Q=";

View file

@ -7,44 +7,43 @@
}:
let
inherit (lib) mkIf;
inherit (pkgs.stdenv.hostPlatform) system;
inherit (config.systemConf) username;
hyprlandEnabled = config.programs.hyprland.enable;
in
{
programs.hyprland = {
enable = config.systemConf.hyprland.enable;
withUWSM = false;
package = inputs.hyprland.packages."${pkgs.system}".hyprland;
portalPackage = inputs.hyprland.packages.${pkgs.system}.xdg-desktop-portal-hyprland;
};
config = mkIf config.programs.hyprland.enable {
programs.hyprland = {
withUWSM = false;
package = inputs.hyprland.packages."${system}".hyprland;
portalPackage = inputs.hyprland.packages.${system}.xdg-desktop-portal-hyprland;
};
environment.sessionVariables = mkIf hyprlandEnabled {
NIXOS_OZONE_WL = "1";
WLR_NO_HARDWARE_CURSORS = "1";
};
environment.sessionVariables = {
NIXOS_OZONE_WL = "1";
WLR_NO_HARDWARE_CURSORS = "1";
};
environment.systemPackages = mkIf hyprlandEnabled (
with pkgs;
[
pyprland
hyprsunset
hyprpicker
hyprshot
kitty
]
);
environment.systemPackages = (
with pkgs;
[
pyprland
hyprsunset
hyprpicker
hyprshot
]
);
nix = mkIf hyprlandEnabled {
settings = {
substituters = [ "https://hyprland.cachix.org" ];
trusted-public-keys = [
"hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="
];
nix = {
settings = {
substituters = [ "https://hyprland.cachix.org" ];
trusted-public-keys = [
"hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="
];
};
};
home-manager.users."${username}" = {
imports = [ ../../home/user/hyprland.nix ];
};
};
home-manager.users."${username}" = mkIf hyprlandEnabled {
imports = [ ../../home/user/hyprland.nix ];
};
}

297
system/modules/mango.nix Normal file
View file

@ -0,0 +1,297 @@
{
config,
lib,
...
}:
let
inherit (lib)
mkIf
removePrefix
concatStringsSep
mapAttrsToList
;
in
{
config = mkIf config.programs.mango.enable {
home-manager.sharedModules = [
(
{ config, ... }:
let
wmCfg = config.wm;
bindCfg = wmCfg.keybinds;
in
{
wm.keybinds = {
mod = "SUPER";
separator = ",";
hypr-type = true;
};
wayland.windowManager.mango = {
enable = true;
settings =
let
keybinds = concatStringsSep "\n" (
mapAttrsToList (n: v: "bind=${n},spawn,${v}") (bindCfg.spawn-repeat // bindCfg.spawn)
);
in
''
# Window effect
blur=0
blur_layer=0
blur_optimized=1
blur_params_num_passes = 2
blur_params_radius = 5
blur_params_noise = 0.02
blur_params_brightness = 0.9
blur_params_contrast = 0.9
blur_params_saturation = 1.2
shadows = 0
layer_shadows = 0
shadow_only_floating = 1
shadows_size = 10
shadows_blur = 15
shadows_position_x = 0
shadows_position_y = 0
shadowscolor= 0x000000ff
border_radius=${toString wmCfg.border.radius}
no_radius_when_single=0
focused_opacity=${toString wmCfg.window.opacity}
unfocused_opacity=${toString wmCfg.window.opacity}
# Animation Configuration(support type:zoom,slide)
# tag_animation_direction: 1-horizontal,0-vertical
animations=1
layer_animations=1
animation_type_open=slide
animation_type_close=slide
animation_fade_in=1
animation_fade_out=1
tag_animation_direction=1
zoom_initial_ratio=0.3
zoom_end_ratio=0.8
fadein_begin_opacity=0.5
fadeout_begin_opacity=0.8
animation_duration_move=500
animation_duration_open=400
animation_duration_tag=350
animation_duration_close=800
animation_duration_focus=0
animation_curve_open=0.46,1.0,0.29,1
animation_curve_move=0.46,1.0,0.29,1
animation_curve_tag=0.46,1.0,0.29,1
animation_curve_close=0.08,0.92,0,1
animation_curve_focus=0.46,1.0,0.29,1
animation_curve_opafadeout=0.5,0.5,0.5,0.5
animation_curve_opafadein=0.46,1.0,0.29,1
# Scroller Layout Setting
scroller_structs=20
scroller_default_proportion=0.8
scroller_focus_center=0
scroller_prefer_center=0
edge_scroller_pointer_focus=1
scroller_default_proportion_single=1.0
scroller_proportion_preset=0.5,0.8,1.0
# Master-Stack Layout Setting
new_is_master=1
default_mfact=0.55
default_nmaster=1
smartgaps=0
# Overview Setting
hotarea_size=10
enable_hotarea=1
ov_tab_mode=0
overviewgappi=5
overviewgappo=30
# Misc
no_border_when_single=0
axis_bind_apply_timeout=100
focus_on_activate=1
idleinhibit_ignore_visible=0
sloppyfocus=1
warpcursor=1
focus_cross_monitor=0
focus_cross_tag=0
enable_floating_snap=0
snap_distance=30
cursor_size=24
drag_tile_to_tile=1
# keyboard
repeat_rate=${toString wmCfg.input.keyboard.repeat-rate}
repeat_delay=${toString wmCfg.input.keyboard.repeat-delay}
numlockon=0
xkb_rules_layout=us
# Trackpad
# need relogin to make it apply
disable_trackpad=0
tap_to_click=1
tap_and_drag=1
drag_lock=1
trackpad_natural_scrolling=0
disable_while_typing=1
left_handed=0
middle_button_emulation=0
swipe_min_threshold=1
# mouse
# need relogin to make it apply
mouse_natural_scrolling=0
# Appearance
gappih=5
gappiv=5
gappoh=10
gappov=10
scratchpad_width_ratio=0.8
scratchpad_height_ratio=0.9
borderpx=4
rootcolor=0x${removePrefix "#" wmCfg.border.active.to}ff
bordercolor=0x${removePrefix "#" wmCfg.border.active.from}ff
focuscolor=0x${removePrefix "#" wmCfg.border.active.to}ff
maximizescreencolor=0x89aa61ff
urgentcolor=0xad401fff
scratchpadcolor=0x516c93ff
globalcolor=0xb153a7ff
overlaycolor=0x14a57cff
# layout support:
# tile,scroller,grid,deck,monocle,center_tile,vertical_tile,vertical_scroller
tagrule=id:1,layout_name:tile
tagrule=id:2,layout_name:tile
tagrule=id:3,layout_name:tile
tagrule=id:4,layout_name:tile
tagrule=id:5,layout_name:tile
tagrule=id:6,layout_name:tile
tagrule=id:7,layout_name:tile
tagrule=id:8,layout_name:tile
tagrule=id:9,layout_name:tile
# Key Bindings
# key name refer to `xev` or `wev` command output,
# mod keys name: super,ctrl,alt,shift,none
${keybinds}
# exit
bind=${bindCfg.close-window},killclient
# switch window focus
bind=${bindCfg.switch-window-focus},focusstack,next
bind=${bindCfg.move-window-focus.left},focusdir,left
bind=${bindCfg.move-window-focus.right},focusdir,right
bind=${bindCfg.move-window-focus.up},focusdir,up
bind=${bindCfg.move-window-focus.down},focusdir,down
# swap window
bind=${bindCfg.move-window.up},exchange_client,up
bind=${bindCfg.move-window.down},exchange_client,down
bind=${bindCfg.move-window.left},exchange_client,left
bind=${bindCfg.move-window.right},exchange_client,right
# switch window status
bind=SUPER,g,toggleglobal,
bind=${bindCfg.toggle-overview},toggleoverview,
bind=${bindCfg.toggle-float},togglefloating,
bind=${bindCfg.maximize-column},togglemaximizescreen,
# bind=${bindCfg.toggle-fullscreen},togglefullscreen,
bind=${bindCfg.toggle-fullscreen},togglefakefullscreen,
bind=${bindCfg.minimize},minimized,
# bind=SUPER,o,toggleoverlay,
bind=${bindCfg.restore-minimize},restore_minimized
bind=${bindCfg.toggle-scratchpad},toggle_scratchpad
# scroller layout
bind=${bindCfg.expand-column-to-available-width},set_proportion,1.0
bind=${bindCfg.switch-preset-column-width},switch_proportion_preset,
# switch layout
bind=${bindCfg.switch-layout},switch_layout
# tag switch
bind=SUPER,Left,viewtoleft,0
bind=CTRL,Left,viewtoleft_have_client,0
bind=SUPER,Right,viewtoright,0
bind=CTRL,Right,viewtoright_have_client,0
bind=CTRL+SUPER,Left,tagtoleft,0
bind=CTRL+SUPER,Right,tagtoright,0
bind=${bindCfg.focus-workspace-prefix},1,view,1,0
bind=${bindCfg.focus-workspace-prefix},2,view,2,0
bind=${bindCfg.focus-workspace-prefix},3,view,3,0
bind=${bindCfg.focus-workspace-prefix},4,view,4,0
bind=${bindCfg.focus-workspace-prefix},5,view,5,0
bind=${bindCfg.focus-workspace-prefix},6,view,6,0
bind=${bindCfg.focus-workspace-prefix},7,view,7,0
bind=${bindCfg.focus-workspace-prefix},8,view,8,0
bind=${bindCfg.focus-workspace-prefix},9,view,9,0
# tag: move client to the tag and focus it
# tagsilent: move client to the tag and not focus it
# bind=Alt,1,tagsilent,1
bind=Alt,1,tag,1,0
bind=Alt,2,tag,2,0
bind=Alt,3,tag,3,0
bind=Alt,4,tag,4,0
bind=Alt,5,tag,5,0
bind=Alt,6,tag,6,0
bind=Alt,7,tag,7,0
bind=Alt,8,tag,8,0
bind=Alt,9,tag,9,0
# monitor switch
bind=${bindCfg.move-monitor-focus.left},focusmon,left
bind=${bindCfg.move-monitor-focus.right},Right,focusmon,right
bind=SUPER+Alt,Left,tagmon,left
bind=SUPER+Alt,Right,tagmon,right
# gaps
# bind=ALT+SHIFT,X,incgaps,1
# bind=ALT+SHIFT,Z,incgaps,-1
# bind=ALT+SHIFT,R,togglegaps
# movewin
bind=CTRL+SHIFT,Up,movewin,+0,-50
bind=CTRL+SHIFT,Down,movewin,+0,+50
bind=CTRL+SHIFT,Left,movewin,-50,+0
bind=CTRL+SHIFT,Right,movewin,+50,+0
# resizewin
bind=CTRL+ALT,Up,resizewin,+0,-50
bind=CTRL+ALT,Down,resizewin,+0,+50
bind=CTRL+ALT,Left,resizewin,-50,+0
bind=CTRL+ALT,Right,resizewin,+50,+0
# Mouse Button Bindings
# NONE mode key only work in ov mode
mousebind=SUPER,btn_left,moveresize,curmove
mousebind=NONE,btn_middle,togglemaximizescreen,0
mousebind=SUPER,btn_right,moveresize,curresize
mousebind=NONE,btn_left,toggleoverview,1
mousebind=NONE,btn_right,killclient,0
# Axis Bindings
axisbind=SUPER,UP,viewtoleft_have_client
axisbind=SUPER,DOWN,viewtoright_have_client
# layer rule
layerrule=animation_type_open:zoom,layer_name:rofi
layerrule=animation_type_close:zoom,layer_name:rofi
'';
autostart_sh = ''
${wmCfg.exec-once}
'';
};
}
)
];
};
}

View file

@ -7,308 +7,264 @@
let
inherit (lib)
getExe
mkIf
pipe
mapAttrs
;
inherit (builtins) fetchurl genList listToAttrs;
inherit (config.systemConf) username;
# nvidia-offload-enabled = config.hardware.nvidia.prime.offload.enableOffloadCmd;
prefix = "nvidia-offload";
terminal = "ghostty";
browser = "zen-twilight";
brightnessStep = toString 10;
volumeStep = toString 4;
execOnceScript = pkgs.writeShellScript "startupExec" ''
# Fix nemo open in terminal
dconf write /org/cinnamon/desktop/applications/terminal/exec "''\'${terminal}''\'" &
dconf write /org/cinnamon/desktop/applications/terminal/exec-arg "''\'''\'" &
# Hint dark theme
dconf write /org/gnome/desktop/interface/color-scheme '"prefer-dark"' &
systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME &
'';
niri_peekaboo = fetchurl {
url = "https://raw.githubusercontent.com/heyoeyo/niri_tweaks/refs/heads/main/niri_peekaboo.py";
sha256 = "sha256:0l1x0bsa9vr089jhzgcz3xbh1hg15sw6njb91q0j9pdbrp2ym3dc";
};
in
{
programs.niri = {
enable = true;
package = pkgs.niri-unstable;
};
home-manager.users."${username}" =
{
osConfig,
config,
...
}:
let
rofiWall = import ../../home/scripts/rofiwall.nix { inherit config pkgs; };
rbwSelector = import ../../home/scripts/rbwSelector.nix { inherit pkgs; };
rNiri = pkgs.writeShellScriptBin "rNiri" ''
NIRI_SOCKET="/run/user/1000/$(ls /run/user/1000 | grep niri | head -n 1)" niri $@
'';
toggleWlogout = pkgs.writeShellScript "toggleWlogout" ''
if ${pkgs.busybox}/bin/pgrep wlogout > /dev/null; then
${pkgs.busybox}/bin/pkill wlogout
else
${config.programs.wlogout.package}/bin/wlogout --protocol layer-shell
fi
'';
in
with config.lib.niri.actions;
{
home.packages = with pkgs; [
nautilus # xdg-desktop-portal-gnome file picker
rNiri
];
xdg.portal = {
extraPortals = with pkgs; [ xdg-desktop-portal-gtk ];
};
services.nfsm.enable = true;
programs.niri.package = osConfig.programs.niri.package;
programs.niri.settings = {
spawn-at-startup = [
{ argv = [ "${execOnceScript}" ]; }
];
screenshot-path = "~/Pictures/Screenshots/%Y-%m-%d_%H-%M-%S.png";
prefer-no-csd = true;
xwayland-satellite = {
enable = true;
path = getExe pkgs.xwayland-satellite-unstable;
};
animations = {
workspace-switch.kind.spring = {
damping-ratio = 1.0;
stiffness = 1000;
epsilon = 0.0001;
};
window-open.kind.easing = {
duration-ms = 150;
curve = "ease-out-expo";
};
window-close.kind.easing = {
duration-ms = 150;
curve = "ease-out-quad";
};
window-resize.kind.spring = {
damping-ratio = 1.0;
stiffness = 800;
epsilon = 0.0001;
};
};
layout.border = {
enable = true;
width = 4;
active.gradient = {
from = "#24273A";
to = "#EBDBB2";
angle = 45;
in' = "oklab";
relative-to = "window";
};
inactive.gradient = {
from = "#24273A";
to = "#24273A";
angle = 45;
in' = "oklab";
relative-to = "window";
};
};
window-rules = [
# Global
{
geometry-corner-radius =
let
round = 12.0;
in
{
bottom-left = round;
bottom-right = round;
top-left = round;
top-right = round;
};
clip-to-geometry = true;
opacity = 1.0;
draw-border-with-background = false;
}
# Float
{
matches = [
{ app-id = "^xdg-desktop-portal-gtk$"; }
{ app-id = "^(org.gnome.Nautilus)$"; }
{ app-id = "^(org.gnome.Loupe)$"; }
];
open-floating = true;
}
];
input = {
focus-follows-mouse = {
max-scroll-amount = "90%";
enable = true;
};
mouse.accel-speed = -0.1;
keyboard = {
repeat-delay = 250;
repeat-rate = 35;
};
};
binds =
let
sh = spawn "sh" "-c";
in
{
# ==== Launch ==== #
"Mod+Return".action = sh "${prefix} ${terminal}";
"Mod+F".action = sh "${browser}";
"Mod+E".action = sh "${prefix} ${terminal} -e yazi";
"Mod+Ctrl+P".action = spawn "${rbwSelector}";
"Mod+Ctrl+M".action = spawn "${toggleWlogout}";
# Rofi
"Mod+Ctrl+W".action = spawn "${rofiWall}";
"Alt+Space".action = spawn "rofi" "-config" "~/.config/rofi/apps.rasi" "-show" "drun";
"Mod+Period".action = spawn "rofi" "-modi" "emoji" "-show" "emoji";
"Mod+Ctrl+C".action = spawn "rofi" "-modi" "calc" "-show" "calc" "-no-show-match" "-no-sort";
# ==== Media ==== #
"XF86AudioPrev".action = spawn "playerctl" "previous";
"XF86AudioNext".action = spawn "playerctl" "next";
"Mod+Ctrl+Comma".action = spawn "playerctl" "previous";
"Mod+Ctrl+Period".action = spawn "playerctl" "next";
"XF86AudioPlay".action = spawn "playerctl" "play-pause";
"XF86AudioStop".action = spawn "playerctl" "stop";
"XF86AudioMute".action = spawn "wpctl" "set-mute" "@DEFAULT_SINK@" "toggle";
"XF86AudioRaiseVolume".action =
sh "wpctl set-mute @DEFAULT_SINK@ 0 && wpctl set-volume @DEFAULT_SINK@ ${volumeStep}%+";
"XF86AudioLowerVolume".action =
sh "wpctl set-mute @DEFAULT_SINK@ 0 && wpctl set-volume @DEFAULT_SINK@ ${volumeStep}%-";
"XF86MonBrightnessDown".action = spawn "brightnessctl set ${brightnessStep}%-";
"XF86MonBrightnessUp".action = spawn "brightnessctl set ${brightnessStep}%+";
# ==== Movement ==== #
# Mouse Scroll
"Mod+WheelScrollDown" = {
cooldown-ms = 150;
action = focus-workspace-down;
};
"Mod+WheelScrollUp" = {
cooldown-ms = 150;
action = focus-workspace-up;
};
"Mod+Shift+WheelScrollDown" = {
cooldown-ms = 150;
action = focus-column-or-monitor-right;
};
"Mod+Shift+WheelScrollUp" = {
cooldown-ms = 150;
action = focus-column-or-monitor-left;
};
"Mod+WheelScrollRight".action = focus-column-right;
"Mod+WheelScrollLeft".action = focus-column-left;
# Touchpad
"Mod+TouchpadScrollDown" = {
cooldown-ms = 150;
action = focus-window-or-workspace-down;
};
"Mod+TouchpadScrollUp" = {
cooldown-ms = 150;
action = focus-window-or-workspace-up;
};
# Monitor Focus
"Mod+Ctrl+H".action = focus-monitor-left;
"Mod+Ctrl+L".action = focus-monitor-right;
# Workspace Focus
"Mod+Ctrl+J".action = focus-workspace-down;
"Mod+Ctrl+K".action = focus-workspace-up;
# General Focus
"Mod+J".action = focus-window-or-workspace-down;
"Mod+K".action = focus-window-or-workspace-up;
"Mod+H".action = focus-column-or-monitor-left;
"Mod+L".action = focus-column-or-monitor-right;
# Workspace Move
"Mod+Ctrl+Shift+J".action = move-workspace-down;
"Mod+Ctrl+Shift+K".action = move-workspace-up;
# Window & Column Move
"Mod+Shift+J".action = move-window-down-or-to-workspace-down;
"Mod+Shift+K".action = move-window-up-or-to-workspace-up;
"Mod+Shift+L".action = move-column-right-or-to-monitor-right;
"Mod+Shift+H".action = move-column-left-or-to-monitor-left;
# Window Comsume
"Mod+Ctrl+Shift+L".action = consume-or-expel-window-right;
"Mod+Ctrl+Shift+H".action = consume-or-expel-window-left;
# ==== Action ==== #
# General
"Mod+C".action = center-window;
"Mod+O".action = toggle-overview;
"Mod+Q".action = close-window;
"F11".action = if config.services.nfsm.enable then (spawn "nfsm-cli") else fullscreen-window;
"Mod+Shift+slash".action = show-hotkey-overlay;
"Mod+Ctrl+Shift+P".action = spawn "${getExe pkgs.python312}" "${niri_peekaboo}";
# Column Scale
"Mod+W".action = switch-preset-column-width;
"Mod+S".action = switch-preset-window-height;
"Mod+P".action = expand-column-to-available-width;
"Mod+M".action = maximize-column;
"Mod+Ctrl+S".action = reset-window-height;
# Float
"Mod+V".action = toggle-window-floating;
"Mod+Ctrl+V".action = switch-focus-between-floating-and-tiling;
# Screenshot
"Mod+Shift+S".action.screenshot = [ { show-pointer = false; } ];
"Ctrl+Shift+S".action.screenshot-window = [
{
write-to-disk = false;
}
];
"Mod+Ctrl+Shift+S".action.screenshot-screen = [
{
write-to-disk = false;
}
];
}
# Map Mod+{1 ~ 9} to workspace{1 ~ 9}
// (pipe 9 [
(x: genList (i: i + 1) x)
(
x:
map (i: {
name = "Mod+${toString i}";
value.action = focus-workspace i;
}) x
)
(x: listToAttrs x)
]);
};
config = mkIf config.programs.niri.enable {
programs.niri = {
package = pkgs.niri-unstable;
};
home-manager.users."${username}" =
{
osConfig,
config,
...
}:
let
rNiri = pkgs.writeShellScriptBin "rNiri" ''
NIRI_SOCKET="/run/user/1000/$(ls /run/user/1000 | grep niri | head -n 1)" niri $@
'';
wmCfg = config.wm;
bindCfg = wmCfg.keybinds;
in
with config.lib.niri.actions;
{
home.packages = with pkgs; [
nautilus # xdg-desktop-portal-gnome file picker
rNiri
];
xdg.portal = {
extraPortals = with pkgs; [ xdg-desktop-portal-gtk ];
};
services.nfsm.enable = true;
programs.niri.package = osConfig.programs.niri.package;
programs.niri.settings = {
spawn-at-startup = [
{ argv = [ "${wmCfg.exec-once}" ]; }
];
screenshot-path = "~/Pictures/Screenshots/%Y-%m-%d_%H-%M-%S.png";
prefer-no-csd = true;
xwayland-satellite = {
enable = true;
path = getExe pkgs.xwayland-satellite-unstable;
};
animations = {
workspace-switch.kind.spring = {
damping-ratio = 1.0;
stiffness = 1000;
epsilon = 0.0001;
};
window-open.kind.easing = {
duration-ms = 150;
curve = "ease-out-expo";
};
window-close.kind.easing = {
duration-ms = 150;
curve = "ease-out-quad";
};
window-resize.kind.spring = {
damping-ratio = 1.0;
stiffness = 800;
epsilon = 0.0001;
};
};
layout.border = {
enable = true;
width = 4;
active.gradient = {
from = wmCfg.border.active.from;
to = wmCfg.border.active.to;
angle = wmCfg.border.active.angle;
in' = "oklab";
relative-to = "window";
};
inactive.gradient = {
from = wmCfg.border.inactive.from;
to = wmCfg.border.inactive.to;
angle = wmCfg.border.inactive.angle;
in' = "oklab";
relative-to = "window";
};
};
window-rules = [
# Global
{
geometry-corner-radius =
let
round = wmCfg.border.radius + 0.0;
in
{
bottom-left = round;
bottom-right = round;
top-left = round;
top-right = round;
};
clip-to-geometry = true;
opacity = 1.0;
draw-border-with-background = false;
}
# Float
{
matches = [
{ app-id = "^xdg-desktop-portal-gtk$"; }
{ app-id = "^(org.gnome.Nautilus)$"; }
{ app-id = "^(org.gnome.Loupe)$"; }
];
open-floating = true;
}
];
input = {
focus-follows-mouse = {
max-scroll-amount = "90%";
enable = true;
};
mouse.accel-speed = -0.1;
keyboard = {
repeat-delay = wmCfg.input.keyboard.repeat-delay;
repeat-rate = wmCfg.input.keyboard.repeat-rate;
};
};
binds =
let
sh = spawn "sh" "-c";
spawnKeybinds = mapAttrs (name: value: {
action = sh value;
}) (wmCfg.keybinds.spawn-repeat // wmCfg.keybinds.spawn);
in
spawnKeybinds
// {
# ==== Movement ==== #
# Mouse Scroll
"Mod+WheelScrollDown" = {
cooldown-ms = 150;
action = focus-workspace-down;
};
"Mod+WheelScrollUp" = {
cooldown-ms = 150;
action = focus-workspace-up;
};
"Mod+Shift+WheelScrollDown" = {
cooldown-ms = 150;
action = focus-column-or-monitor-right;
};
"Mod+Shift+WheelScrollUp" = {
cooldown-ms = 150;
action = focus-column-or-monitor-left;
};
"Mod+WheelScrollRight".action = focus-column-right;
"Mod+WheelScrollLeft".action = focus-column-left;
# Touchpad
"Mod+TouchpadScrollDown" = {
cooldown-ms = 150;
action = focus-window-or-workspace-down;
};
"Mod+TouchpadScrollUp" = {
cooldown-ms = 150;
action = focus-window-or-workspace-up;
};
# Monitor Focus
"${bindCfg.move-monitor-focus.left}".action = focus-monitor-left;
"${bindCfg.move-monitor-focus.right}".action = focus-monitor-right;
# Workspace Focus
"${bindCfg.move-workspace-focus.next}".action = focus-workspace-down;
"${bindCfg.move-workspace-focus.prev}".action = focus-workspace-up;
# General Focus
"${bindCfg.move-window-focus.down}".action = focus-window-or-workspace-down;
"${bindCfg.move-window-focus.up}".action = focus-window-or-workspace-up;
"${bindCfg.move-window-focus.left}".action = focus-column-or-monitor-left;
"${bindCfg.move-window-focus.right}".action = focus-column-or-monitor-right;
# Workspace Move
"${bindCfg.move-workspace.down}".action = move-workspace-down;
"${bindCfg.move-workspace.up}".action = move-workspace-up;
# Window & Column Move
"${bindCfg.move-window.down}".action = move-window-down-or-to-workspace-down;
"${bindCfg.move-window.up}".action = move-window-up-or-to-workspace-up;
"${bindCfg.move-window.right}".action = move-column-right-or-to-monitor-right;
"${bindCfg.move-window.left}".action = move-column-left-or-to-monitor-left;
# Window Comsume
"${bindCfg.consume-window.right}".action = consume-or-expel-window-right;
"${bindCfg.consume-window.left}".action = consume-or-expel-window-left;
# ==== Action ==== #
# General
"${bindCfg.center-window}".action = center-window;
"${bindCfg.toggle-overview}".action = toggle-overview;
"${bindCfg.close-window}".action = close-window;
"${bindCfg.toggle-fullscreen}".action =
if config.services.nfsm.enable then (spawn "nfsm-cli") else fullscreen-window;
"Mod+Shift+slash".action = show-hotkey-overlay;
"Mod+Ctrl+Shift+P".action = spawn "${getExe pkgs.python312}" "${niri_peekaboo}";
# Column Scale
"${bindCfg.switch-preset-column-width}".action = switch-preset-column-width;
"${bindCfg.switch-preset-window-height}".action = switch-preset-window-height;
"${bindCfg.expand-column-to-available-width}".action = expand-column-to-available-width;
"${bindCfg.maximize-column}".action = maximize-column;
"${bindCfg.reset-window-height}".action = reset-window-height;
# Float
"${bindCfg.toggle-float}".action = toggle-window-floating;
"${bindCfg.switch-focus-between-floating-and-tiling}".action =
switch-focus-between-floating-and-tiling;
# Screenshot
"${bindCfg.screenshot.area}".action.screenshot = [ { show-pointer = false; } ];
"${bindCfg.screenshot.window}".action.screenshot-window = [
{
write-to-disk = false;
}
];
"${bindCfg.screenshot.screen}".action.screenshot-screen = [
{
write-to-disk = false;
}
];
}
# Map Mod+{1 ~ 9} to workspace{1 ~ 9}
// (pipe 9 [
(x: genList (i: i + 1) x)
(
x:
map (i: {
name = "${bindCfg.focus-workspace-prefix}+${toString i}";
value.action = focus-workspace i;
}) x
)
(x: listToAttrs x)
]);
};
};
};
}

View file

@ -11,6 +11,7 @@
../polkit.nix
../hyprland.nix
../niri.nix
../mango.nix
];
programs.gdk-pixbuf.modulePackages = [ pkgs.librsvg ];

View file

@ -3,7 +3,13 @@ let
inherit (config.systemConf) username;
inherit (builtins) mapAttrs;
inherit (lib) mkForce;
inherit (lib)
mkForce
removePrefix
concatStringsSep
mapAttrsToList
mkIf
;
in
{
@ -15,7 +21,13 @@ in
# ================================= #
home-manager.users.${username} =
{ config, ... }:
{ osConfig, config, ... }:
let
wmCfg = config.wm;
bindCfg = wmCfg.keybinds;
mod = wmCfg.keybinds.mod;
sep = wmCfg.keybinds.separator;
in
{
# ==== Disabled Services ==== #
services.swww.enable = mkForce false; # Wallpaper
@ -29,6 +41,21 @@ in
"QT_QPA_PLATFORMTHEME=gtk3"
];
wm.keybinds.spawn-repeat = {
# ==== Media ==== #
"XF86AudioPrev" = ''noctalia "media" "previous"'';
"XF86AudioNext" = ''noctalia "media" "next"'';
"${mod}${sep}CTRL${sep}COMMA" = ''noctalia "media" "previous"'';
"${mod}${sep}CTRL${sep}PERIOD" = ''noctalia "media" "next"'';
"XF86AudioPlay" = ''noctalia "media" "playPause"'';
"XF86AudioStop" = ''noctalia "media" "pause"'';
"XF86AudioMute" = ''noctalia "volume" "muteOutput"'';
"XF86AudioRaiseVolume" = ''noctalia "volume" "increase"'';
"XF86AudioLowerVolume" = ''noctalia "volume" "decrease"'';
"XF86MonBrightnessDown" = ''noctalia "brightness" "decrease"'';
"XF86MonBrightnessUp" = ''noctalia "brightness" "increase"'';
};
programs.noctalia-shell = {
enable = true;
systemd.enable = true;
@ -43,7 +70,7 @@ in
];
position = "top_center";
sortByMostUsed = true;
terminalCommand = "ghostty -e";
terminalCommand = "${wmCfg.app.terminal.run}";
useApp2Unit = false;
viewMode = "list";
};
@ -244,7 +271,7 @@ in
};
};
programs.niri.settings =
programs.niri.settings = mkIf osConfig.programs.niri.enable (
with config.lib.niri.actions;
let
noctalia = spawn "noctalia-shell" "ipc" "call";
@ -252,33 +279,310 @@ in
{
binds = mapAttrs (name: value: mkForce value) {
# Core
"Mod+Slash".action = noctalia "controlCenter" "toggle";
"Alt+Space".action = noctalia "launcher" "toggle";
"Mod+Ctrl+M".action = noctalia "lockScreen" "lock";
"${bindCfg.toggle-control-center}".action = noctalia "controlCenter" "toggle";
"${bindCfg.toggle-launcher}".action = noctalia "launcher" "toggle";
"${bindCfg.lock-screen}".action = noctalia "lockScreen" "lock";
# Utilities
"Mod+Comma".action = noctalia "launcher" "clipboard";
"Mod+Period".action = noctalia "launcher" "emoji";
"Mod+F12".action = noctalia "screenRecorder" "toggle";
"Mod+N".action = noctalia "notifications" "toggleHistory";
"Mod+Ctrl+N".action = noctalia "notifications" "toggleDND";
"Mod+Ctrl+W".action = noctalia "wallpaper" "toggle";
"Mod+Ctrl+C".action = noctalia "launcher" "calculator";
"Mod+Ctrl+Slash".action = noctalia "wallpaper" "random";
"${bindCfg.clipboard-history}".action = noctalia "launcher" "clipboard";
"${bindCfg.emoji}".action = noctalia "launcher" "emoji";
"${bindCfg.screen-recorder}".action = noctalia "screenRecorder" "toggle";
"${bindCfg.notification-center}".action = noctalia "notifications" "toggleHistory";
"${bindCfg.toggle-dont-disturb}".action = noctalia "notifications" "toggleDND";
"${bindCfg.wallpaper-selector}".action = noctalia "wallpaper" "toggle";
"${bindCfg.calculator}".action = noctalia "launcher" "calculator";
"${bindCfg.wallpaper-random}".action = noctalia "wallpaper" "random";
# Media
"XF86AudioPlay".action = noctalia "media" "playPause";
"XF86AudioStop".action = noctalia "media" "pause";
"XF86AudioPrev".action = noctalia "media" "previous";
"XF86AudioNext".action = noctalia "media" "next";
"Mod+Ctrl+Comma".action = noctalia "media" "previous";
"Mod+Ctrl+Period".action = noctalia "media" "next";
"${bindCfg.media.prev}".action = noctalia "media" "previous";
"${bindCfg.media.next}".action = noctalia "media" "next";
"XF86AudioMute".action = noctalia "volume" "muteOutput";
"XF86AudioRaiseVolume".action = noctalia "volume" "increase";
"XF86AudioLowerVolume".action = noctalia "volume" "decrease";
"XF86MonBrightnessDown".action = noctalia "brightness" "decrease";
"XF86MonBrightnessUp".action = noctalia "brightness" "increase";
};
};
}
);
wayland.windowManager.mango.settings = mkIf osConfig.programs.mango.enable (
mkForce (
let
keybinds = concatStringsSep "\n" (
mapAttrsToList (n: v: "bind=${n},spawn,${v}") (bindCfg.spawn-repeat // bindCfg.spawn)
);
in
''
# Window effect
blur=0
blur_layer=0
blur_optimized=1
blur_params_num_passes = 2
blur_params_radius = 5
blur_params_noise = 0.02
blur_params_brightness = 0.9
blur_params_contrast = 0.9
blur_params_saturation = 1.2
shadows = 0
layer_shadows = 0
shadow_only_floating = 1
shadows_size = 10
shadows_blur = 15
shadows_position_x = 0
shadows_position_y = 0
shadowscolor= 0x000000ff
border_radius=${toString wmCfg.border.radius}
no_radius_when_single=0
focused_opacity=${toString wmCfg.window.opacity}
unfocused_opacity=${toString wmCfg.window.opacity}
# Animation Configuration(support type:zoom,slide)
# tag_animation_direction: 1-horizontal,0-vertical
animations=1
layer_animations=1
animation_type_open=slide
animation_type_close=slide
animation_fade_in=1
animation_fade_out=1
tag_animation_direction=1
zoom_initial_ratio=0.3
zoom_end_ratio=0.8
fadein_begin_opacity=0.5
fadeout_begin_opacity=0.8
animation_duration_move=500
animation_duration_open=400
animation_duration_tag=350
animation_duration_close=800
animation_duration_focus=0
animation_curve_open=0.46,1.0,0.29,1
animation_curve_move=0.46,1.0,0.29,1
animation_curve_tag=0.46,1.0,0.29,1
animation_curve_close=0.08,0.92,0,1
animation_curve_focus=0.46,1.0,0.29,1
animation_curve_opafadeout=0.5,0.5,0.5,0.5
animation_curve_opafadein=0.46,1.0,0.29,1
# Scroller Layout Setting
scroller_structs=20
scroller_default_proportion=0.8
scroller_focus_center=0
scroller_prefer_center=0
edge_scroller_pointer_focus=1
scroller_default_proportion_single=1.0
scroller_proportion_preset=0.5,0.8,1.0
# Master-Stack Layout Setting
new_is_master=1
default_mfact=0.55
default_nmaster=1
smartgaps=0
# Overview Setting
hotarea_size=10
enable_hotarea=1
ov_tab_mode=0
overviewgappi=5
overviewgappo=30
# Misc
no_border_when_single=0
axis_bind_apply_timeout=100
focus_on_activate=1
idleinhibit_ignore_visible=0
sloppyfocus=1
warpcursor=1
focus_cross_monitor=0
focus_cross_tag=0
enable_floating_snap=0
snap_distance=30
cursor_size=24
drag_tile_to_tile=1
# keyboard
repeat_rate=${toString wmCfg.input.keyboard.repeat-rate}
repeat_delay=${toString wmCfg.input.keyboard.repeat-delay}
numlockon=0
xkb_rules_layout=us
# Trackpad
# need relogin to make it apply
disable_trackpad=0
tap_to_click=1
tap_and_drag=1
drag_lock=1
trackpad_natural_scrolling=0
disable_while_typing=1
left_handed=0
middle_button_emulation=0
swipe_min_threshold=1
# mouse
# need relogin to make it apply
mouse_natural_scrolling=0
# Appearance
gappih=5
gappiv=5
gappoh=10
gappov=10
scratchpad_width_ratio=0.8
scratchpad_height_ratio=0.9
borderpx=4
rootcolor=0x${removePrefix "#" wmCfg.border.active.to}ff
bordercolor=0x${removePrefix "#" wmCfg.border.active.from}ff
focuscolor=0x${removePrefix "#" wmCfg.border.active.to}ff
maximizescreencolor=0x89aa61ff
urgentcolor=0xad401fff
scratchpadcolor=0x516c93ff
globalcolor=0xb153a7ff
overlaycolor=0x14a57cff
# layout support:
# tile,scroller,grid,deck,monocle,center_tile,vertical_tile,vertical_scroller
tagrule=id:1,layout_name:tile
tagrule=id:2,layout_name:tile
tagrule=id:3,layout_name:tile
tagrule=id:4,layout_name:tile
tagrule=id:5,layout_name:tile
tagrule=id:6,layout_name:tile
tagrule=id:7,layout_name:tile
tagrule=id:8,layout_name:tile
tagrule=id:9,layout_name:tile
# Key Bindings
# key name refer to `xev` or `wev` command output,
# mod keys name: super,ctrl,alt,shift,none
${keybinds}
# exit
bind=${bindCfg.close-window},killclient
# switch window focus
bind=${bindCfg.switch-window-focus},focusstack,next
bind=${bindCfg.move-window-focus.left},focusdir,left
bind=${bindCfg.move-window-focus.right},focusdir,right
bind=${bindCfg.move-window-focus.up},focusdir,up
bind=${bindCfg.move-window-focus.down},focusdir,down
# swap window
bind=${bindCfg.move-window.up},exchange_client,up
bind=${bindCfg.move-window.down},exchange_client,down
bind=${bindCfg.move-window.left},exchange_client,left
bind=${bindCfg.move-window.right},exchange_client,right
# switch window status
bind=SUPER,g,toggleglobal,
bind=${bindCfg.toggle-overview},toggleoverview,
bind=${bindCfg.toggle-float},togglefloating,
bind=${bindCfg.maximize-column},togglemaximizescreen,
# bind=${bindCfg.toggle-fullscreen},togglefullscreen,
bind=${bindCfg.toggle-fullscreen},togglefakefullscreen,
bind=${bindCfg.minimize},minimized,
# bind=SUPER,o,toggleoverlay,
bind=${bindCfg.restore-minimize},restore_minimized
bind=${bindCfg.toggle-scratchpad},toggle_scratchpad
# scroller layout
bind=${bindCfg.expand-column-to-available-width},set_proportion,1.0
bind=${bindCfg.switch-preset-column-width},switch_proportion_preset,
# switch layout
bind=${bindCfg.switch-layout},switch_layout
# tag switch
bind=SUPER,Left,viewtoleft,0
bind=CTRL,Left,viewtoleft_have_client,0
bind=SUPER,Right,viewtoright,0
bind=CTRL,Right,viewtoright_have_client,0
bind=CTRL+SUPER,Left,tagtoleft,0
bind=CTRL+SUPER,Right,tagtoright,0
bind=${bindCfg.focus-workspace-prefix},1,view,1,0
bind=${bindCfg.focus-workspace-prefix},2,view,2,0
bind=${bindCfg.focus-workspace-prefix},3,view,3,0
bind=${bindCfg.focus-workspace-prefix},4,view,4,0
bind=${bindCfg.focus-workspace-prefix},5,view,5,0
bind=${bindCfg.focus-workspace-prefix},6,view,6,0
bind=${bindCfg.focus-workspace-prefix},7,view,7,0
bind=${bindCfg.focus-workspace-prefix},8,view,8,0
bind=${bindCfg.focus-workspace-prefix},9,view,9,0
# tag: move client to the tag and focus it
# tagsilent: move client to the tag and not focus it
# bind=Alt,1,tagsilent,1
bind=Alt,1,tag,1,0
bind=Alt,2,tag,2,0
bind=Alt,3,tag,3,0
bind=Alt,4,tag,4,0
bind=Alt,5,tag,5,0
bind=Alt,6,tag,6,0
bind=Alt,7,tag,7,0
bind=Alt,8,tag,8,0
bind=Alt,9,tag,9,0
# monitor switch
bind=${bindCfg.move-monitor-focus.left},focusmon,left
bind=${bindCfg.move-monitor-focus.right},Right,focusmon,right
bind=SUPER+Alt,Left,tagmon,left
bind=SUPER+Alt,Right,tagmon,right
# gaps
# bind=ALT+SHIFT,X,incgaps,1
# bind=ALT+SHIFT,Z,incgaps,-1
# bind=ALT+SHIFT,R,togglegaps
# movewin
bind=CTRL+SHIFT,Up,movewin,+0,-50
bind=CTRL+SHIFT,Down,movewin,+0,+50
bind=CTRL+SHIFT,Left,movewin,-50,+0
bind=CTRL+SHIFT,Right,movewin,+50,+0
# resizewin
bind=CTRL+ALT,Up,resizewin,+0,-50
bind=CTRL+ALT,Down,resizewin,+0,+50
bind=CTRL+ALT,Left,resizewin,-50,+0
bind=CTRL+ALT,Right,resizewin,+50,+0
# Mouse Button Bindings
# NONE mode key only work in ov mode
mousebind=SUPER,btn_left,moveresize,curmove
mousebind=NONE,btn_middle,togglemaximizescreen,0
mousebind=SUPER,btn_right,moveresize,curresize
mousebind=NONE,btn_left,toggleoverview,1
mousebind=NONE,btn_right,killclient,0
# Axis Bindings
axisbind=SUPER,UP,viewtoleft_have_client
axisbind=SUPER,DOWN,viewtoright_have_client
# layer rule
layerrule=animation_type_open:zoom,layer_name:rofi
layerrule=animation_type_close:zoom,layer_name:rofi
# Core
${bindCfg.toggle-control-center},spawn,noctalia "controlCenter" "toggle";
${bindCfg.toggle-launcher},spawn,noctalia "launcher" "toggle";
${bindCfg.lock-screen},spawn,noctalia "lockScreen" "lock";
# Utilities
${bindCfg.clipboard-history},spawn,noctalia "launcher" "clipboard";
${bindCfg.emoji},spawn,noctalia "launcher" "emoji";
${bindCfg.screen-recorder},spawn,noctalia "screenRecorder" "toggle";
${bindCfg.notification-center},spawn,noctalia "notifications" "toggleHistory";
${bindCfg.toggle-dont-disturb},spawn,noctalia "notifications" "toggleDND";
${bindCfg.wallpaper-selector},spawn,noctalia "wallpaper" "toggle";
${bindCfg.calculator},spawn,noctalia "launcher" "calculator";
${bindCfg.wallpaper-random},spawn,noctalia "wallpaper" "random";
''
)
);
};
}

View file

@ -14,7 +14,7 @@
let
inherit (lib) mkIf;
logFilePath = "${config.services.stalwart-mail.dataDir}/logs";
logFilePath = "${config.services.stalwart.dataDir}/logs";
in
{
services.postgresql = {
@ -32,13 +32,13 @@ in
systemd.tmpfiles.rules =
let
inherit (config.users.users.stalwart-mail) name group;
inherit (config.users.users.stalwart) name group;
in
[
"d ${logFilePath} 0750 ${name} ${group} - "
];
services.stalwart-mail = {
services.stalwart = {
enable = true;
openFirewall = true;
settings = {